NewsBlur/clients/ios/Classes/StoryDetailObjCViewController.m

2627 lines
125 KiB
Mathematica
Raw Normal View History

//
// StoryDetailObjCViewController.m
// NewsBlur
//
// Created by Samuel Clay on 6/24/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
#import "StoryDetailObjCViewController.h"
#import "NewsBlurAppDelegate.h"
#import "FontSettingsViewController.h"
#import "UserProfileViewController.h"
#import "ShareViewController.h"
#import "Utilities.h"
#import "NSString+HTML.h"
#import "NBContainerViewController.h"
2012-07-26 13:54:45 -07:00
#import "DataUtilities.h"
#import "FMDatabase.h"
#import "SBJson4.h"
#import "StringHelper.h"
#import "StoriesCollection.h"
#import "UIView+ViewController.h"
#import "JNWThrottledBlock.h"
#import "NewsBlur-Swift.h"
#define iPadPro12 ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && ([UIScreen mainScreen].bounds.size.height == 1366 || [UIScreen mainScreen].bounds.size.width == 1366))
#define iPadPro10 ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && ([UIScreen mainScreen].bounds.size.height == 1112 || [UIScreen mainScreen].bounds.size.width == 1112))
@interface StoryDetailObjCViewController ()
@property (nonatomic, strong) NSString *fullStoryHTML;
@end
@implementation StoryDetailObjCViewController
@synthesize appDelegate;
@synthesize activeStoryId;
@synthesize activeStory;
@synthesize innerView;
@synthesize webView;
@synthesize feedTitleGradient;
@synthesize noStoryMessage;
@synthesize pullingScrollview;
@synthesize pageIndex;
@synthesize storyHUD;
@synthesize inTextView;
@synthesize isRecentlyUnread;
2012-07-26 18:07:29 -07:00
#pragma mark -
#pragma mark View boilerplate
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
}
return self;
}
- (void)dealloc {
[self.webView.scrollView removeObserver:self forKeyPath:@"contentOffset"];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (NSString *)description {
NSString *page = appDelegate.storyPagesViewController.currentPage == self ? @"currentPage" : appDelegate.storyPagesViewController.previousPage == self ? @"previousPage" : appDelegate.storyPagesViewController.nextPage == self ? @"nextPage" : @"unattached page";
return [NSString stringWithFormat:@"%@", page];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
2012-07-23 15:27:20 -07:00
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback
error:nil];
WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSString *videoPlayback = [preferences stringForKey:@"video_playback"];
configuration.allowsInlineMediaPlayback = ![videoPlayback isEqualToString:@"fullscreen"];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
[self.view addSubview:self.webView];
self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.webView.navigationDelegate = self;
// self.webView.scalesPageToFit = YES;
self.webView.allowsLinkPreview = YES;
// self.webView.multipleTouchEnabled = NO;
[self.webView.scrollView setAlwaysBounceVertical:appDelegate.storyPagesViewController.isHorizontal];
[self.webView.scrollView setDelaysContentTouches:NO];
[self.webView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal];
[self.webView.scrollView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight)];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
[self.webView.scrollView addObserver:self forKeyPath:@"contentOffset"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil];
[self.appDelegate prepareWebView:self.webView completionHandler:nil];
[self clearWebView];
// UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc]
// initWithTarget:self action:@selector(showOriginalStory:)];
// [self.webView addGestureRecognizer:pinchGesture];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(doubleTap:)];
doubleTapGesture.numberOfTapsRequired = 2;
doubleTapGesture.delegate = self;
[self.webView addGestureRecognizer:doubleTapGesture];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(tap:)];
tapGesture.numberOfTapsRequired = 1;
tapGesture.delegate = self;
[tapGesture requireGestureRecognizerToFail:doubleTapGesture];
[self.webView addGestureRecognizer:tapGesture];
UITapGestureRecognizer *doubleDoubleTapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(doubleTap:)];
doubleDoubleTapGesture.numberOfTouchesRequired = 2;
doubleDoubleTapGesture.numberOfTapsRequired = 2;
doubleDoubleTapGesture.delegate = self;
[self.webView addGestureRecognizer:doubleDoubleTapGesture];
2015-12-07 16:09:49 -08:00
2020-08-25 21:46:23 -07:00
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc]
initWithTarget:self action:@selector(pinchGesture:)];
[self.webView addGestureRecognizer:pinchGesture];
}
2015-12-07 16:09:49 -08:00
[[ThemeManager themeManager] addThemeGestureRecognizerToView:self.webView];
// This makes the theme gesture work reliably, but makes scrolling more "sticky", so isn't acceptable:
// UIGestureRecognizer *themeGesture = [[ThemeManager themeManager] addThemeGestureRecognizerToView:self.webView];
// [self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:themeGesture];
self.pageIndex = -2;
self.inTextView = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(tapAndHold:)
name:@"TapAndHoldNotification"
object:nil];
_orientation = self.view.window.windowScene.interfaceOrientation;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// 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));
if (inDoubleTap) {
self.webView.scrollView.scrollEnabled = NO;
[self performSelector:@selector(deferredEnableScrolling) withObject:nil afterDelay:0.0];
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
2014-09-19 16:58:56 -07:00
// NSLog(@"Should conflict? \n\tgesture:%@ \n\t other:%@",
// gestureRecognizer, otherGestureRecognizer);
return YES;
}
- (void)tap:(UITapGestureRecognizer *)gestureRecognizer {
// NSLog(@"Gesture tap: %ld (%ld) - %d", (long)gestureRecognizer.state, (long)UIGestureRecognizerStateEnded, inDoubleTap);
if (gestureRecognizer.state == UIGestureRecognizerStateEnded && gestureRecognizer.numberOfTouches == 1 && 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));
2020-08-27 15:08:46 -07:00
[self.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"]) {
2020-08-27 15:08:46 -07:00
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'id');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *identifier, NSError *error) {
[self.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-"]) {
[self.appDelegate.storyPagesViewController tappedStory];
}
}];
}];
return;
}
// Ignore links, videos, and iframes (e.g. embedded YouTube videos).
if (![@[@"A", @"VIDEO", @"IFRAME"] containsObject:tagName]) {
[self.appDelegate.storyPagesViewController tappedStory];
}
}];
}
}
- (void)doubleTap:(UITapGestureRecognizer *)gestureRecognizer {
// NSLog(@"Gesture double tap: %d (%d) - %d", gestureRecognizer.state, UIGestureRecognizerStateEnded, inDoubleTap);
if (gestureRecognizer.state == UIGestureRecognizerStateEnded && inDoubleTap) {
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
BOOL openOriginal = NO;
BOOL showText = NO;
BOOL markUnread = NO;
BOOL saveStory = NO;
if (gestureRecognizer.numberOfTouches == 2) {
NSString *twoFingerTap = [preferences stringForKey:@"two_finger_double_tap"];
if ([twoFingerTap isEqualToString:@"open_original_story"]) {
openOriginal = YES;
} else if ([twoFingerTap isEqualToString:@"show_original_text"]) {
showText = YES;
} else if ([twoFingerTap isEqualToString:@"mark_unread"]) {
markUnread = YES;
} else if ([twoFingerTap isEqualToString:@"save_story"]) {
saveStory = YES;
}
} else {
NSString *doubleTap = [preferences stringForKey:@"double_tap_story"];
if ([doubleTap isEqualToString:@"open_original_story"]) {
openOriginal = YES;
} else if ([doubleTap isEqualToString:@"show_original_text"]) {
showText = YES;
} else if ([doubleTap isEqualToString:@"mark_unread"]) {
markUnread = YES;
} else if ([doubleTap isEqualToString:@"save_story"]) {
saveStory = YES;
}
}
if (openOriginal) {
[self showOriginalStory:gestureRecognizer];
} else if (showText) {
[self fetchTextView];
} else if (markUnread) {
[appDelegate.storiesCollection toggleStoryUnread];
[appDelegate.feedDetailViewController reloadData];
} else if (saveStory) {
[appDelegate.storiesCollection toggleStorySaved];
[appDelegate.feedDetailViewController reloadData];
}
inDoubleTap = NO;
2015-11-12 15:30:40 -08:00
[self performSelector:@selector(deferredEnableScrolling) withObject:nil afterDelay:0.0];
appDelegate.storyPagesViewController.autoscrollActive = NO;
}
}
- (void)pinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state != UIGestureRecognizerStateEnded) {
return;
}
appDelegate.storyPagesViewController.forceNavigationBarShown = gestureRecognizer.scale < 1;
[appDelegate.storyPagesViewController changedFullscreen];
}
- (void)screenEdgeSwipe:(UITapGestureRecognizer *)gestureRecognizer {
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
BOOL swipeEnabled = [[userPreferences stringForKey:@"story_detail_swipe_left_edge"]
isEqualToString:@"pop_to_story_list"];
2019-01-16 19:15:07 -08:00
// if (swipeEnabled && gestureRecognizer.state == UIGestureRecognizerStateBegan) {
// [appDelegate.storyPagesViewController setNavigationBarHidden:NO];
// }
2019-01-16 19:15:07 -08:00
if (swipeEnabled && gestureRecognizer.state == UIGestureRecognizerStateEnded) {
[appDelegate hideStoryDetailView];
}
}
2015-11-12 15:30:40 -08:00
- (void)deferredEnableScrolling {
self.webView.scrollView.scrollEnabled = YES;
}
- (void)viewDidDisappear:(BOOL)animated {
2014-09-18 11:25:51 -07:00
[super viewDidDisappear:animated];
if (!appDelegate.showingSafariViewController &&
appDelegate.feedsNavigationController.visibleViewController != (UIViewController *)appDelegate.shareViewController &&
appDelegate.feedsNavigationController.visibleViewController != (UIViewController *)appDelegate.trainerViewController &&
appDelegate.feedsNavigationController.visibleViewController != (UIViewController *)appDelegate.originalStoryViewController) {
[self clearStory];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (!self.isPhoneOrCompact) {
[appDelegate.feedDetailViewController.view endEditing:YES];
}
[self storeScrollPosition:NO];
self.fullStoryHTML = nil;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (!self.isPhoneOrCompact) {
[appDelegate.feedDetailViewController.view endEditing:YES];
}
if (_orientation != self.view.window.windowScene.interfaceOrientation) {
_orientation = self.view.window.windowScene.interfaceOrientation;
NSLog(@"Found stale orientation in story detail: %@", NSStringFromCGSize(self.view.bounds.size));
}
if (!self.hasStory) {
[self drawStory];
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Fix the position and size; can probably remove this once all views use auto layout
CGRect viewFrame = self.view.frame;
CGSize superSize = self.view.superview.bounds.size;
if (viewFrame.size.height > superSize.height) {
self.view.frame = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.width, superSize.height);
}
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
scrollPct = self.webView.scrollView.contentOffset.y / self.webView.scrollView.contentSize.height;
// NSLog(@"Current scroll is %2.2f%% (offset %.0f - height %.0f)", scrollPct*100, self.webView.scrollView.contentOffset.y,
// self.webView.scrollView.contentSize.height);
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
self->_orientation = self.view.window.windowScene.interfaceOrientation;
[self changeWebViewWidth];
[self drawFeedGradient];
[self scrollToLastPosition:NO];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
}];
}
- (void)viewWillLayoutSubviews {
UIInterfaceOrientation orientation = self.view.window.windowScene.interfaceOrientation;
[super viewWillLayoutSubviews];
dispatch_async(dispatch_get_main_queue(), ^{
[self.appDelegate.storyPagesViewController layoutForInterfaceOrientation:orientation];
[self changeWebViewWidth];
[self drawFeedGradient];
});
2015-12-01 16:51:18 -08:00
// NSLog(@"viewWillLayoutSubviews: %.2f", self.webView.scrollView.bounds.size.width);
}
- (void)layoutForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (interfaceOrientation != _orientation) {
_orientation = interfaceOrientation;
[self changeWebViewWidth];
[self drawFeedGradient];
[self drawStory];
}
}
- (BOOL)isPhoneOrCompact {
2020-08-25 21:46:23 -07:00
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone || self.appDelegate.isCompactWidth;
}
// allow keyboard comands
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark -
2012-11-08 17:39:32 -08:00
#pragma mark Story setup
- (void)initStory {
appDelegate.inStoryDetail = YES;
self.noStoryMessage.hidden = YES;
self.inTextView = NO;
2012-11-08 17:39:32 -08:00
[appDelegate hideShareView:NO];
2012-11-08 17:39:32 -08:00
}
- (void)loadHTMLString:(NSString *)html {
static NSURL *baseURL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
baseURL = [NSBundle mainBundle].bundleURL;
});
[self.webView loadHTMLString:html baseURL:baseURL];
}
- (void)hideNoStoryMessage {
self.noStoryMessage.hidden = YES;
}
2012-11-08 17:39:32 -08:00
- (void)drawStory {
UIInterfaceOrientation orientation = self.view.window.windowScene.interfaceOrientation;
2013-03-04 17:15:50 -08:00
[self drawStory:NO withOrientation:orientation];
}
- (void)drawStory:(BOOL)force withOrientation:(UIInterfaceOrientation)orientation {
if (!force && [self.activeStoryId isEqualToString:[self.activeStory objectForKey:@"story_hash"]]) {
2015-12-01 16:51:18 -08:00
// NSLog(@"Already drawn story, drawing anyway: %@", [self.activeStory objectForKey:@"story_title"]);
// return;
}
if (self.activeStory == nil) {
return;
}
scrollPct = 0;
hasScrolled = NO;
NSString *shareBarString = [self getShareBar];
NSString *commentString = [self getComments];
NSString *headerString;
NSString *sharingHtmlString;
NSString *footerString;
NSString *fontStyleClass = @"";
NSString *customStyle = @"";
2013-11-23 13:03:14 -08:00
NSString *fontSizeClass = @"NB-";
2014-05-15 18:06:56 -07:00
NSString *lineSpacingClass = @"NB-line-spacing-";
2017-11-15 17:11:37 -08:00
NSString *premiumOnlyClass = (self.inTextView && !appDelegate.isPremium) ? @"NB-premium-only" : @"";
NSString *storyContent = [self.activeStory objectForKey:@"story_content"];
if (self.inTextView && [self.activeStory objectForKey:@"original_text"]) {
storyContent = [self.activeStory objectForKey:@"original_text"];
}
NSString *changes = self.activeStory[@"story_changes"];
if (changes != nil) {
storyContent = changes;
}
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
2017-11-15 17:11:37 -08:00
NSString *premiumTextString = [NSString stringWithFormat:@"<div class=\"NB-feed-story-premium-only-divider\"></div>"
2017-11-15 17:11:44 -08:00
"<div class=\"NB-feed-story-premium-only-text\">The full Text view is a <a href=\"http://ios.newsblur.com/premium\">premium feature</a></div>"];
2017-11-15 17:11:37 -08:00
fontStyleClass = [userPreferences stringForKey:@"fontStyle"];
if (!fontStyleClass) {
2017-04-11 17:36:10 -07:00
fontStyleClass = @"GothamNarrow-Book";
}
2013-11-23 13:03:14 -08:00
fontSizeClass = [fontSizeClass stringByAppendingString:[userPreferences stringForKey:@"story_font_size"]];
if (![fontStyleClass hasPrefix:@"NB-"]) {
customStyle = [NSString stringWithFormat:@" style='font-family: %@;'", fontStyleClass];
}
2014-05-15 18:06:56 -07:00
if ([userPreferences stringForKey:@"story_line_spacing"]){
lineSpacingClass = [lineSpacingClass stringByAppendingString:[userPreferences stringForKey:@"story_line_spacing"]];
} else {
lineSpacingClass = [lineSpacingClass stringByAppendingString:@"medium"];
}
int contentWidth = CGRectGetWidth(self.webView.scrollView.bounds);
NSString *contentWidthClass;
2015-12-01 16:51:18 -08:00
// NSLog(@"Drawing story: %@ / %d", [self.activeStory objectForKey:@"story_title"], contentWidth);
#if TARGET_OS_MACCATALYST
// CATALYST: probably will want to add custom CSS for Macs.
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-12-wide NB-width-768";
#else
if (UIInterfaceOrientationIsLandscape(orientation) && !self.isPhoneOrCompact) {
if (iPadPro12) {
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-12-wide";
} else if (iPadPro10) {
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-10-wide";
} else {
contentWidthClass = @"NB-ipad-wide";
}
} else if (!UIInterfaceOrientationIsLandscape(orientation) && !self.isPhoneOrCompact) {
if (iPadPro12) {
contentWidthClass = @"NB-ipad-narrow NB-ipad-pro-12-narrow";
} else if (iPadPro10) {
contentWidthClass = @"NB-ipad-narrow NB-ipad-pro-10-narrow";
} else {
contentWidthClass = @"NB-ipad-narrow";
}
} else if (UIInterfaceOrientationIsLandscape(orientation) && self.isPhoneOrCompact) {
contentWidthClass = @"NB-iphone-wide";
} else {
contentWidthClass = @"NB-iphone";
}
contentWidthClass = [NSString stringWithFormat:@"%@ NB-width-%d",
contentWidthClass, (int)floorf(CGRectGetWidth(self.view.frame))];
#endif
// Replace image urls that are locally cached, even when online
// NSString *storyHash = [self.activeStory objectForKey:@"story_hash"];
// NSArray *imageUrls = [appDelegate.activeCachedImages objectForKey:storyHash];
// if (imageUrls) {
// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
// NSString *storyImagesDirectory = [[paths objectAtIndex:0]
// stringByAppendingPathComponent:@"story_images"];
// for (NSString *imageUrl in imageUrls) {
// NSString *cachedImage = [storyImagesDirectory
// stringByAppendingPathComponent:[Utilities md5:imageUrl]];
// storyContent = [storyContent
// stringByReplacingOccurrencesOfString:imageUrl
// withString:cachedImage];
// }
// }
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[self.activeStory
objectForKey:@"story_feed_id"]];
NSDictionary *feed = [appDelegate getFeed:feedIdStr];
NSString *storyClassSuffix = @"";
if ([feed[@"is_newsletter"] isEqualToNumber:[NSNumber numberWithInt:1]]) {
storyClassSuffix = @" NB-newsletter";
}
NSString *riverClass = (appDelegate.storiesCollection.isRiverView ||
appDelegate.storiesCollection.isSocialView ||
appDelegate.storiesCollection.isSavedView ||
appDelegate.storiesCollection.isReadView) ?
@"NB-river" : @"NB-non-river";
2018-10-22 15:14:12 -07:00
NSString *themeStyle = [NSString stringWithFormat:@"<link rel=\"stylesheet\" type=\"text/css\" id=\"NB-theme-style\" href=\"storyDetailView%@.css\">", [ThemeManager themeManager].themeCSSSuffix];
2015-12-07 16:09:49 -08:00
// set up layout values based on iPad/iPhone
headerString = [NSString stringWithFormat:@
2015-12-07 16:09:49 -08:00
"<link rel=\"stylesheet\" type=\"text/css\" href=\"storyDetailView.css\">%@"
"<meta name=\"viewport\" id=\"viewport\" content=\"width=%d, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\"/>",
2015-12-07 16:09:49 -08:00
themeStyle, contentWidth];
footerString = [NSString stringWithFormat:@
"<script src=\"zepto.js\"></script>"
2014-01-06 15:02:12 -08:00
"<script src=\"fitvid.js\"></script>"
"<script src=\"storyDetailView.js\"></script>"
"<script src=\"fastTouch.js\"></script>"];
sharingHtmlString = [self getSideOptions];
NSString *storyHeader = [self getHeader];
NSString *htmlTop = [NSString stringWithFormat:@
"<!DOCTYPE html>\n"
"<html>"
"<head>%@</head>" // header string
"<body id=\"story_pane\" class=\"%@ %@\">"
2017-11-15 17:11:37 -08:00
" <div class=\"%@\" id=\"NB-premium-check\">"
" <div class=\"%@\" id=\"NB-font-style\"%@>"
2014-05-15 18:06:56 -07:00
" <div class=\"%@\" id=\"NB-font-size\">"
" <div class=\"%@\" id=\"NB-line-spacing\">"
" <div id=\"NB-header-container\">%@</div>" // storyHeader
" %@", // shareBar
headerString,
contentWidthClass,
riverClass,
2017-11-15 17:11:37 -08:00
premiumOnlyClass,
fontStyleClass,
customStyle,
fontSizeClass,
2014-05-15 18:06:56 -07:00
lineSpacingClass,
2012-12-11 17:53:17 -08:00
storyHeader,
shareBarString
];
NSString *htmlBottom = [NSString stringWithFormat:@
" </div>" // line-spacing
" </div>" // font-size
" </div>" // font-style
2017-11-15 17:11:37 -08:00
" </div>" // premium check
"</body>"
"</html>"
];
NSString *htmlContent = [NSString stringWithFormat:@
"%@" // header
" <div id=\"NB-story\" class=\"NB-story%@\">%@</div>"
2017-11-15 17:11:37 -08:00
" <div class=\"NB-text-view-premium-only\">%@</div>"
" <div id=\"NB-sideoptions-container\">%@</div>"
" <div id=\"NB-comments-wrapper\">"
" %@" // friends comments
" </div>"
" %@"
"%@", // footer
htmlTop,
storyClassSuffix,
storyContent,
2017-11-15 17:11:37 -08:00
premiumTextString,
sharingHtmlString,
commentString,
footerString,
htmlBottom
];
NSString *htmlTopAndBottom = [htmlTop stringByAppendingString:htmlBottom];
// NSLog(@"\n\n\n\nStory html (%@):\n\n\n%@\n\n\n", self.activeStory[@"story_title"], htmlContent);
self.hasStory = NO;
self.fullStoryHTML = htmlContent;
dispatch_async(dispatch_get_main_queue(), ^{
2015-12-01 16:51:18 -08:00
// NSLog(@"Drawing Story: %@", [self.activeStory objectForKey:@"story_title"]);
[self loadHTMLString:htmlTopAndBottom];
[self.appDelegate.storyPagesViewController setTextButton:(StoryDetailViewController *)self];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self loadStory];
});
self.activeStoryId = [self.activeStory objectForKey:@"story_hash"];
}
- (void)drawFeedGradient {
BOOL shouldHideStatusBar = appDelegate.storyPagesViewController.shouldHideStatusBar;
CGFloat yOffset = -1;
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[self.activeStory
objectForKey:@"story_feed_id"]];
NSDictionary *feed = [appDelegate getFeed:feedIdStr];
if (appDelegate.storyPagesViewController.currentlyTogglingNavigationBar && !appDelegate.storyPagesViewController.isNavigationBarHidden) {
yOffset -= 25;
}
if (self.feedTitleGradient) {
[self.feedTitleGradient removeFromSuperview];
self.feedTitleGradient = nil;
}
self.feedTitleGradient = [appDelegate
makeFeedTitleGradient:feed
withRect:CGRectMake(0, yOffset, CGRectGetWidth(self.view.bounds), 21)]; // 1024 hack for self.webView.frame.size.width
2015-09-22 18:20:48 -07:00
self.feedTitleGradient.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.feedTitleGradient.tag = FEED_TITLE_GRADIENT_TAG; // Not attached yet. Remove old gradients, first.
for (UIView *subview in self.webView.subviews) {
if (subview.tag == FEED_TITLE_GRADIENT_TAG) {
[subview removeFromSuperview];
}
}
if (appDelegate.storiesCollection.isRiverView ||
appDelegate.storiesCollection.isSocialView ||
appDelegate.storiesCollection.isSavedView ||
appDelegate.storiesCollection.isReadView) {
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(20, 0, 0, 0);
} else {
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(9, 0, 0, 0);
}
[self.webView insertSubview:feedTitleGradient aboveSubview:self.webView.scrollView];
if (appDelegate.storyPagesViewController.view.safeAreaInsets.top > 0.0 && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone && shouldHideStatusBar) {
feedTitleGradient.alpha = appDelegate.storyPagesViewController.isNavigationBarHidden ? 1 : 0;
[UIView animateWithDuration:0.3 animations:^{
self.feedTitleGradient.alpha = self.appDelegate.storyPagesViewController.isNavigationBarHidden ? 0 : 1;
}];
}
}
2012-11-08 17:39:32 -08:00
- (void)showStory {
id storyId = [self.activeStory objectForKey:@"story_hash"];
[appDelegate.storiesCollection pushReadStory:storyId];
[appDelegate resetShareComments];
2012-11-08 17:39:32 -08:00
}
- (void)clearStory {
self.activeStoryId = nil;
if (self.activeStory) self.activeStoryId = [self.activeStory objectForKey:@"story_hash"];
[self clearWebView];
2013-10-17 11:21:37 -07:00
[MBProgressHUD hideHUDForView:self.webView animated:NO];
2012-11-08 17:39:32 -08:00
}
- (void)hideStory {
self.activeStoryId = nil;
2012-11-08 17:39:32 -08:00
self.webView.hidden = YES;
self.noStoryMessage.hidden = NO;
[self.activityIndicator stopAnimating];
2012-11-08 17:39:32 -08:00
}
#pragma mark -
#pragma mark Story layout
- (void)clearWebView {
self.hasStory = NO;
2016-01-07 21:12:58 -08:00
self.view.backgroundColor = UIColorFromRGB(NEWSBLUR_WHITE_COLOR);
self.webView.hidden = YES;
self.activityIndicator.color = UIColorFromRGB(NEWSBLUR_BLACK_COLOR);
[self.activityIndicator startAnimating];
NSString *themeStyle = [ThemeManager themeManager].themeCSSSuffix;
if (themeStyle.length) {
themeStyle = [NSString stringWithFormat:@"<link rel=\"stylesheet\" type=\"text/css\" href=\"storyDetailView%@.css\">", themeStyle];
}
NSString *html = [NSString stringWithFormat:@"<html>"
"<head><link rel=\"stylesheet\" type=\"text/css\" href=\"storyDetailView.css\">%@</head>" // header string
"<body></body>"
"</html>", themeStyle];
[self loadHTMLString:html];
}
- (NSString *)getHeader {
2012-12-19 15:37:36 -08:00
NSString *feedId = [NSString stringWithFormat:@"%@", [self.activeStory
objectForKey:@"story_feed_id"]];
NSString *storyAuthor = @"";
if ([[self.activeStory objectForKey:@"story_authors"] class] != [NSNull class] &&
[[self.activeStory objectForKey:@"story_authors"] length]) {
NSString *author = [NSString stringWithFormat:@"%@",
2016-03-07 22:46:34 -08:00
[[[[self.activeStory objectForKey:@"story_authors"] stringByReplacingOccurrencesOfString:@"\"" withString:@""]
stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"]
stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"]];
if (author && author.length) {
int authorScore = [[[[appDelegate.storiesCollection.activeClassifiers objectForKey:feedId]
objectForKey:@"authors"]
objectForKey:author] intValue];
storyAuthor = [NSString stringWithFormat:@"<span class=\"NB-middot\">&middot;</span><a href=\"http://ios.newsblur.com/classify-author/%@\" "
"class=\"NB-story-author %@\" id=\"NB-story-author\"><div class=\"NB-highlight\"></div>%@</a>",
author,
authorScore > 0 ? @"NB-story-author-positive" : authorScore < 0 ? @"NB-story-author-negative" : @"",
author];
}
}
NSString *storyTags = @"";
if ([self.activeStory objectForKey:@"story_tags"]) {
NSArray *tagArray = [self.activeStory objectForKey:@"story_tags"];
if ([tagArray count] > 0) {
NSMutableArray *tagStrings = [NSMutableArray array];
for (NSString *tag in tagArray) {
int tagScore = [[[[appDelegate.storiesCollection.activeClassifiers objectForKey:feedId]
objectForKey:@"tags"]
objectForKey:tag] intValue];
NSString *tagHtml = [NSString stringWithFormat:@"<a href=\"http://ios.newsblur.com/classify-tag/%@\" "
"class=\"NB-story-tag %@\"><div class=\"NB-highlight\"></div>%@</a>",
tag,
tagScore > 0 ? @"NB-story-tag-positive" : tagScore < 0 ? @"NB-story-tag-negative" : @"",
tag];
[tagStrings addObject:tagHtml];
}
storyTags = [NSString
stringWithFormat:@"<div id=\"NB-story-tags\" class=\"NB-story-tags\">"
"%@"
"</div>",
[tagStrings componentsJoinedByString:@""]];
}
}
NSString *storyStarred = @"";
NSString *storyUserTags = @"";
NSMutableArray *tagStrings = [NSMutableArray array];
if ([self.activeStory objectForKey:@"starred"] && [self.activeStory objectForKey:@"starred_date"]) {
storyStarred = [NSString stringWithFormat:@"<div class=\"NB-story-starred-date\">Saved on %@</div>",
[self.activeStory objectForKey:@"starred_date"]];
if ([self.activeStory objectForKey:@"user_tags"]) {
NSArray *tagArray = [self.activeStory objectForKey:@"user_tags"];
if ([tagArray count] > 0) {
for (NSString *tag in tagArray) {
NSString *tagHtml = [NSString stringWithFormat:@"<a href=\"http://ios.newsblur.com/remove-user-tag/%@\" "
"class=\"NB-user-tag\"><div class=\"NB-highlight\"></div>%@</a>",
tag,
tag];
[tagStrings addObject:tagHtml];
}
}
}
storyUserTags = [NSString
stringWithFormat:@"<div id=\"NB-user-tags\" class=\"NB-user-tags\">"
"%@"
"<a class=\"NB-user-tag NB-add-user-tag\" href=\"http://ios.newsblur.com/add-user-tag/add-user-tag/\"><div class=\"NB-highlight\"></div>Add Tag</a>"
"</div>",
[tagStrings componentsJoinedByString:@""]];
}
NSString *storyUnread = @"";
if (self.isRecentlyUnread && [appDelegate.storiesCollection isStoryUnread:self.activeStory]) {
NSInteger score = [NewsBlurAppDelegate computeStoryScore:[self.activeStory objectForKey:@"intelligence"]];
storyUnread = [NSString stringWithFormat:@"<div class=\"NB-story-unread NB-%@\"></div>",
score > 0 ? @"positive" : score < 0 ? @"negative" : @"neutral"];
}
NSString *storyTitle = [self.activeStory objectForKey:@"story_title"];
NSString *storyPermalink = [self.activeStory objectForKey:@"story_permalink"];
NSMutableDictionary *titleClassifiers = [[appDelegate.storiesCollection.activeClassifiers
objectForKey:feedId]
objectForKey:@"titles"];
for (NSString *titleClassifier in titleClassifiers) {
if ([storyTitle containsString:titleClassifier]) {
int titleScore = [[titleClassifiers objectForKey:titleClassifier] intValue];
storyTitle = [storyTitle
stringByReplacingOccurrencesOfString:titleClassifier
withString:[NSString stringWithFormat:@"<span class=\"NB-story-title-%@\">%@</span>",
titleScore > 0 ? @"positive" : titleScore < 0 ? @"negative" : @"",
titleClassifier]];
}
}
NSString *storyToggleChanges = [self.activeStory[@"has_modifications"] boolValue] ? [NSString stringWithFormat:@"<a href=\"http://ios.newsblur.com/togglechanges\" "
"class=\"NB-story-toggle-changes\" id=\"NB-story-toggle-changes\">%@</a><span class=\"NB-middot\">&middot;</span>", self.activeStory[@"story_changes"] != nil ? @"Hide Changes" : @"Show Changes"] : @"";
NSString *storyDate = [Utilities formatLongDateFromTimestamp:[[self.activeStory
objectForKey:@"story_timestamp"]
integerValue]];
NSString *storyHeader = [NSString stringWithFormat:@
"<div class=\"NB-header\"><div class=\"NB-header-inner\">"
"<div class=\"NB-story-title\">"
" %@"
" <a href=\"%@\" class=\"NB-story-permalink\">%@</a>"
"</div>"
"%@"
"<div class=\"NB-story-date\">%@</div>"
"%@"
"%@"
"%@"
"%@"
"</div></div>",
storyUnread,
storyPermalink,
storyTitle,
storyToggleChanges,
storyDate,
storyAuthor,
storyTags,
storyStarred,
storyUserTags];
return storyHeader;
}
- (NSString *)getSideOptions {
BOOL isSaved = [[self.activeStory objectForKey:@"starred"] boolValue];
BOOL isShared = [[self.activeStory objectForKey:@"shared"] boolValue];
NSString *sideOptions = [NSString stringWithFormat:@
"<div class='NB-sideoptions'>"
"<div class='NB-share-header'></div>"
"<div class='NB-share-wrapper'><div class='NB-share-inner-wrapper'>"
" <div id=\"NB-share-button-id\" class='NB-share-button NB-train-button NB-button'>"
" <a href=\"http://ios.newsblur.com/train\"><div>"
" <span class=\"NB-icon\"></span> Train"
" </div></a>"
" </div>"
" <div id=\"NB-share-button-id\" class='NB-share-button NB-button %@'>"
" <a href=\"http://ios.newsblur.com/share\"><div>"
" <span class=\"NB-icon\"></span> %@"
" </div></a>"
" </div>"
" <div id=\"NB-share-button-id\" class='NB-share-button NB-save-button NB-button %@'>"
" <a href=\"http://ios.newsblur.com/save/save/\"><div>"
" <span class=\"NB-icon\"></span> %@"
" </div></a>"
" </div>"
"</div></div></div>",
isShared ? @"NB-button-active" : @"",
isShared ? @"Shared" : @"Share",
isSaved ? @"NB-button-active" : @"",
isSaved ? @"Saved" : @"Save"
];
return sideOptions;
}
2012-08-10 09:45:44 -07:00
- (NSString *)getAvatars:(NSString *)key {
NSString *avatarString = @"";
NSArray *shareUserIds = [self.activeStory objectForKey:key];
for (int i = 0; i < shareUserIds.count; i++) {
NSDictionary *user = [appDelegate getUser:[[shareUserIds objectAtIndex:i] intValue]];
2012-08-13 15:34:53 -07:00
NSString *avatarClass = @"NB-user-avatar";
if ([key isEqualToString:@"commented_by_public"] ||
[key isEqualToString:@"shared_by_public"]) {
avatarClass = @"NB-public-user NB-user-avatar";
}
NSString *avatar = [NSString stringWithFormat:@
2012-08-13 15:34:53 -07:00
"<div class=\"NB-story-share-profile\"><div class=\"%@\">"
"<a id=\"NB-user-share-bar-%@\" class=\"NB-show-profile\" "
" href=\"http://ios.newsblur.com/show-profile/%@\">"
"<div class=\"NB-highlight\"></div>"
"<img src=\"%@\" />"
"</a>"
"</div></div>",
2012-08-13 15:34:53 -07:00
avatarClass,
2012-07-01 21:58:55 -07:00
[user objectForKey:@"user_id"],
2012-08-01 12:41:02 -07:00
[user objectForKey:@"user_id"],
[user objectForKey:@"photo_url"]];
avatarString = [avatarString stringByAppendingString:avatar];
}
2012-08-10 09:45:44 -07:00
return avatarString;
}
2012-08-07 11:27:00 -07:00
- (NSString *)getComments {
NSString *comments = @"";
if ([self.activeStory objectForKey:@"share_count"] != [NSNull null] &&
[[self.activeStory objectForKey:@"share_count"] intValue] > 0) {
NSDictionary *story = self.activeStory;
NSArray *friendsCommentsArray = [story objectForKey:@"friend_comments"];
2015-09-07 21:39:51 -07:00
NSArray *friendsShareArray = [story objectForKey:@"friend_shares"];
NSArray *publicCommentsArray = [story objectForKey:@"public_comments"];
2013-03-05 12:26:37 -08:00
2013-03-05 17:00:03 -08:00
if ([[story objectForKey:@"comment_count_friends"] intValue] > 0 ) {
comments = [comments stringByAppendingString:@"<div class=\"NB-story-comments-group NB-story-comment-friend-comments\">"];
2013-03-05 17:00:03 -08:00
NSString *commentHeader = [NSString stringWithFormat:@
"<div class=\"NB-story-comments-friends-header-wrapper\">"
" <div class=\"NB-story-comments-friends-header\">%i comment%@</div>"
"</div>",
[[story objectForKey:@"comment_count_friends"] intValue],
[[story objectForKey:@"comment_count_friends"] intValue] == 1 ? @"" : @"s"];
comments = [comments stringByAppendingString:commentHeader];
// add friends comments
comments = [comments stringByAppendingFormat:@"<div class=\"NB-feed-story-comments\">"];
2013-03-05 17:00:03 -08:00
for (int i = 0; i < friendsCommentsArray.count; i++) {
NSString *comment = [self getComment:[friendsCommentsArray objectAtIndex:i]];
comments = [comments stringByAppendingString:comment];
}
comments = [comments stringByAppendingString:@"</div>"];
comments = [comments stringByAppendingString:@"</div>"];
}
2015-09-07 21:39:51 -07:00
NSInteger sharedByFriendsCount = [[story objectForKey:@"shared_by_friends"] count];
if (sharedByFriendsCount > 0 ) {
comments = [comments stringByAppendingString:@"<div class=\"NB-story-comments-group NB-story-comment-friend-shares\">"];
NSString *commentHeader = [NSString stringWithFormat:@
2015-09-07 21:39:51 -07:00
"<div class=\"NB-story-comments-friend-shares-header-wrapper\">"
" <div class=\"NB-story-comments-friends-header\">%ld share%@</div>"
"</div>",
2015-09-07 21:39:51 -07:00
(long)sharedByFriendsCount,
sharedByFriendsCount == 1 ? @"" : @"s"];
comments = [comments stringByAppendingString:commentHeader];
// add friend shares
comments = [comments stringByAppendingFormat:@"<div class=\"NB-feed-story-comments\">"];
2015-09-07 21:39:51 -07:00
for (int i = 0; i < friendsShareArray.count; i++) {
NSString *comment = [self getComment:[friendsShareArray objectAtIndex:i]];
comments = [comments stringByAppendingString:comment];
}
comments = [comments stringByAppendingString:@"</div>"];
comments = [comments stringByAppendingString:@"</div>"];
}
2012-08-07 11:27:00 -07:00
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"show_public_comments"] boolValue] &&
[[story objectForKey:@"comment_count_public"] intValue] > 0 ) {
comments = [comments stringByAppendingString:@"<div class=\"NB-story-comments-group NB-story-comment-public-comments\">"];
2012-08-07 11:27:00 -07:00
NSString *publicCommentHeader = [NSString stringWithFormat:@
"<div class=\"NB-story-comments-public-header-wrapper\">"
2013-03-05 12:26:37 -08:00
" <div class=\"NB-story-comments-public-header\">%i public comment%@</div>"
2012-08-07 11:27:00 -07:00
"</div>",
[[story objectForKey:@"comment_count_public"] intValue],
[[story objectForKey:@"comment_count_public"] intValue] == 1 ? @"" : @"s"];
comments = [comments stringByAppendingString:publicCommentHeader];
2013-03-05 12:26:37 -08:00
comments = [comments stringByAppendingFormat:@"<div class=\"NB-feed-story-comments\">"];
2013-03-05 17:00:03 -08:00
2013-03-05 12:26:37 -08:00
// add public comments
2012-08-07 11:27:00 -07:00
for (int i = 0; i < publicCommentsArray.count; i++) {
NSString *comment = [self getComment:[publicCommentsArray objectAtIndex:i]];
comments = [comments stringByAppendingString:comment];
}
comments = [comments stringByAppendingString:@"</div>"];
comments = [comments stringByAppendingString:@"</div>"];
2012-08-07 11:27:00 -07:00
}
}
return comments;
}
- (NSString *)getShareBar {
2012-08-13 15:34:53 -07:00
NSString *comments = @"<div id=\"NB-share-bar-wrapper\">";
2012-08-10 09:45:44 -07:00
NSString *commentLabel = @"";
NSString *shareLabel = @"";
2012-08-13 15:34:53 -07:00
// NSString *replyStr = @"";
2012-08-10 09:45:44 -07:00
// if ([[self.activeStory objectForKey:@"reply_count"] intValue] == 1) {
2012-08-13 15:34:53 -07:00
// replyStr = [NSString stringWithFormat:@" and <b>1 reply</b>"];
// } else if ([[self.activeStory objectForKey:@"reply_count"] intValue] == 1) {
// replyStr = [NSString stringWithFormat:@" and <b>%@ replies</b>", [self.activeStory objectForKey:@"reply_count"]];
2012-08-13 15:34:53 -07:00
// }
if (![[self.activeStory objectForKey:@"comment_count"] isKindOfClass:[NSNull class]] &&
[[self.activeStory objectForKey:@"comment_count"] intValue]) {
2012-08-10 09:45:44 -07:00
commentLabel = [commentLabel stringByAppendingString:[NSString stringWithFormat:@
"<div class=\"NB-story-comments-label\">"
2012-12-11 17:53:17 -08:00
"%@" // comment count
//"%@" // reply count
2012-08-10 09:45:44 -07:00
"</div>"
"<div class=\"NB-story-share-profiles NB-story-share-profiles-comments\">"
2012-12-11 17:53:17 -08:00
"%@" // friend avatars
"%@" // public avatars
2012-08-10 09:45:44 -07:00
"</div>",
[[self.activeStory objectForKey:@"comment_count"] intValue] == 1
? [NSString stringWithFormat:@"<b>1 comment</b>"] :
[NSString stringWithFormat:@"<b>%@ comments</b>", [self.activeStory objectForKey:@"comment_count"]],
2012-08-10 09:45:44 -07:00
2012-08-13 15:34:53 -07:00
//replyStr,
2012-08-10 09:45:44 -07:00
[self getAvatars:@"commented_by_friends"],
[self getAvatars:@"commented_by_public"]]];
}
if (![[self.activeStory objectForKey:@"share_count"] isKindOfClass:[NSNull class]] &&
[[self.activeStory objectForKey:@"share_count"] intValue]) {
2012-08-10 09:45:44 -07:00
shareLabel = [shareLabel stringByAppendingString:[NSString stringWithFormat:@
2012-08-13 15:34:53 -07:00
"<div class=\"NB-right\">"
"<div class=\"NB-story-share-profiles NB-story-share-profiles-shares\">"
2012-08-10 09:45:44 -07:00
"%@" // friend avatars
"%@" // public avatars
2012-08-13 15:34:53 -07:00
"</div>"
2012-12-11 17:53:17 -08:00
"<div class=\"NB-story-share-label\">"
"%@" // comment count
"</div>"
2012-08-13 15:34:53 -07:00
"</div>",
[self getAvatars:@"shared_by_public"],
[self getAvatars:@"shared_by_friends"],
[[self.activeStory objectForKey:@"share_count"] intValue] == 1
2012-08-10 09:45:44 -07:00
? [NSString stringWithFormat:@"<b>1 share</b>"] :
[NSString stringWithFormat:@"<b>%@ shares</b>", [self.activeStory objectForKey:@"share_count"]]]];
2012-08-10 09:45:44 -07:00
}
2012-08-07 11:27:00 -07:00
if ([self.activeStory objectForKey:@"share_count"] != [NSNull null] &&
[[self.activeStory objectForKey:@"share_count"] intValue] > 0) {
2012-08-02 18:33:55 -07:00
2012-08-09 17:01:37 -07:00
comments = [comments stringByAppendingString:[NSString stringWithFormat:@
"<div class=\"NB-story-shares\">"
2012-12-11 17:53:17 -08:00
"<div class=\"NB-story-comments-shares-teaser-wrapper\">"
"<div class=\"NB-story-comments-shares-teaser\">"
"%@"
"%@"
"</div>"
"</div>"
"</div>",
2012-08-10 09:45:44 -07:00
commentLabel,
shareLabel
2012-08-09 17:01:37 -07:00
]];
2012-06-22 15:53:51 -07:00
}
2012-08-13 15:34:53 -07:00
comments = [comments stringByAppendingString:[NSString stringWithFormat:@"</div>"]];
2012-06-22 15:53:51 -07:00
return comments;
}
2012-06-25 20:28:07 -07:00
- (NSString *)getComment:(NSDictionary *)commentDict {
NSDictionary *user = [appDelegate getUser:[[commentDict objectForKey:@"user_id"] intValue]];
NSString *userAvatarClass = @"NB-user-avatar";
NSString *userReshareString = @"";
NSString *userEditButton = @"";
NSString *userLikeButton = @"";
NSString *commentUserId = [NSString stringWithFormat:@"%@", [commentDict objectForKey:@"user_id"]];
NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]];
NSArray *likingUsersArray = [commentDict objectForKey:@"liking_users"];
NSString *likingUsers = @"";
2012-07-26 13:54:45 -07:00
if ([likingUsersArray count]) {
likingUsers = @"<div class=\"NB-story-comment-likes-icon\"></div>";
for (NSNumber *likingUser in likingUsersArray) {
NSDictionary *sourceUser = [appDelegate getUser:[likingUser intValue]];
NSString *likingUserString = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-likes-user\">"
" <div class=\"NB-user-avatar\"><img src=\"%@\"></div>"
"</div>",
[sourceUser objectForKey:@"photo_url"]];
likingUsers = [likingUsers stringByAppendingString:likingUserString];
}
}
if ([commentUserId isEqualToString:currentUserId]) {
userEditButton = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-edit-button NB-story-comment-share-edit-button NB-button\">"
"<a href=\"http://ios.newsblur.com/edit-share/%@\"><div class=\"NB-story-comment-edit-button-wrapper\">"
"Edit"
"</div></a>"
2012-07-20 18:04:54 -07:00
"</div>",
commentUserId];
} else {
2012-07-26 13:54:45 -07:00
BOOL isInLikingUsers = NO;
for (int i = 0; i < likingUsersArray.count; i++) {
if ([[[likingUsersArray objectAtIndex:i] stringValue] isEqualToString:currentUserId]) {
2012-07-26 13:54:45 -07:00
isInLikingUsers = YES;
break;
}
}
if (isInLikingUsers) {
userLikeButton = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-like-button NB-button selected\">"
"<a href=\"http://ios.newsblur.com/unlike-comment/%@\"><div class=\"NB-story-comment-like-button-wrapper\">"
2012-12-11 17:53:17 -08:00
"<span class=\"NB-favorite-icon\"></span>"
"</div></a>"
2012-07-26 13:54:45 -07:00
"</div>",
commentUserId];
} else {
userLikeButton = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-like-button NB-button\">"
"<a href=\"http://ios.newsblur.com/like-comment/%@\"><div class=\"NB-story-comment-like-button-wrapper\">"
2012-12-11 17:53:17 -08:00
"<span class=\"NB-favorite-icon\"></span>"
"</div></a>"
2012-07-26 13:54:45 -07:00
"</div>",
commentUserId];
}
}
if ([commentDict objectForKey:@"source_user_id"] != [NSNull null]) {
userAvatarClass = @"NB-user-avatar NB-story-comment-reshare";
NSDictionary *sourceUser = [appDelegate getUser:[[commentDict objectForKey:@"source_user_id"] intValue]];
userReshareString = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-reshares\">"
" <div class=\"NB-story-share-profile\">"
" <div class=\"NB-user-avatar\"><img src=\"%@\"></div>"
" </div>"
"</div>",
[sourceUser objectForKey:@"photo_url"]];
}
NSString *commentContent = [self textToHtml:[commentDict objectForKey:@"comments"]];
2012-07-29 13:24:03 -07:00
NSString *comment;
2012-08-09 16:34:59 -07:00
NSString *locationHtml = @"";
NSString *location = [NSString stringWithFormat:@"%@", [user objectForKey:@"location"]];
if (location.length && ![[user objectForKey:@"location"] isKindOfClass:[NSNull class]]) {
2012-08-09 16:34:59 -07:00
locationHtml = [NSString stringWithFormat:@"<div class=\"NB-story-comment-location\">%@</div>", location];
}
2012-07-29 13:24:03 -07:00
if (!self.isPhoneOrCompact) {
2012-07-29 13:24:03 -07:00
comment = [NSString stringWithFormat:@
"<div class=\"NB-story-comment\" id=\"NB-user-comment-%@\">"
"<div class=\"%@\">"
"<a class=\"NB-show-profile\" href=\"http://ios.newsblur.com/show-profile/%@\">"
"<div class=\"NB-highlight\"></div>"
"<img src=\"%@\" />"
"</a>"
"</div>"
2012-07-29 13:24:03 -07:00
"<div class=\"NB-story-comment-author-container\">"
" %@"
" <div class=\"NB-story-comment-username\">%@</div>"
" <div class=\"NB-story-comment-date\">%@ ago</div>"
" <div class=\"NB-story-comment-likes\">%@</div>"
2012-12-11 17:53:17 -08:00
"</div>"
"<div class=\"NB-story-comment-content\">%@</div>"
"%@" // location
"<div class=\"NB-button-wrapper\">"
2012-07-29 13:24:03 -07:00
" <div class=\"NB-story-comment-reply-button NB-button\">"
" <a href=\"http://ios.newsblur.com/reply/%@/%@\"><div class=\"NB-story-comment-reply-button-wrapper\">"
" Reply"
" </div></a>"
2012-07-29 13:24:03 -07:00
" </div>"
2012-12-11 17:53:17 -08:00
" %@" //User Like Button
" %@" //User Edit Button
2012-07-29 13:24:03 -07:00
"</div>"
"%@"
"</div>",
[commentDict objectForKey:@"user_id"],
userAvatarClass,
[commentDict objectForKey:@"user_id"],
[user objectForKey:@"photo_url"],
userReshareString,
[user objectForKey:@"username"],
[commentDict objectForKey:@"shared_date"],
likingUsers,
2012-12-11 17:53:17 -08:00
commentContent,
locationHtml,
2012-08-13 15:34:53 -07:00
[commentDict objectForKey:@"user_id"],
[user objectForKey:@"username"],
2012-12-11 17:53:17 -08:00
userEditButton,
userLikeButton,
[self getReplies:[commentDict objectForKey:@"replies"] forUserId:[commentDict objectForKey:@"user_id"]]];
2012-07-29 13:24:03 -07:00
} else {
comment = [NSString stringWithFormat:@
"<div class=\"NB-story-comment\" id=\"NB-user-comment-%@\">"
"<div class=\"%@\">"
"<a class=\"NB-show-profile\" href=\"http://ios.newsblur.com/show-profile/%@\">"
"<div class=\"NB-highlight\"></div>"
"<img src=\"%@\" />"
"</a>"
"</div>"
2012-07-29 13:24:03 -07:00
"<div class=\"NB-story-comment-author-container\">"
2012-12-11 17:53:17 -08:00
" %@"
2012-07-29 13:24:03 -07:00
" <div class=\"NB-story-comment-username\">%@</div>"
" <div class=\"NB-story-comment-date\">%@ ago</div>"
" <div class=\"NB-story-comment-likes\">%@</div>"
2012-07-29 13:24:03 -07:00
"</div>"
2012-08-13 16:20:23 -07:00
"<div class=\"NB-story-comment-content\">%@</div>"
2012-12-11 17:53:17 -08:00
"%@" // location
"<div class=\"NB-button-wrapper\">"
2012-07-29 13:24:03 -07:00
" <div class=\"NB-story-comment-reply-button NB-button\">"
" <a href=\"http://ios.newsblur.com/reply/%@/%@\"><div class=\"NB-story-comment-reply-button-wrapper\">"
" Reply"
" </div></a>"
2012-07-29 13:24:03 -07:00
" </div>"
2012-12-11 17:53:17 -08:00
" %@" // User Like Button
" %@" // User Edit Button
2012-07-29 13:24:03 -07:00
"</div>"
"%@"
"</div>",
[commentDict objectForKey:@"user_id"],
userAvatarClass,
[commentDict objectForKey:@"user_id"],
[user objectForKey:@"photo_url"],
userReshareString,
[user objectForKey:@"username"],
[commentDict objectForKey:@"shared_date"],
likingUsers,
commentContent,
2012-12-11 17:53:17 -08:00
locationHtml,
2012-08-13 16:20:23 -07:00
[commentDict objectForKey:@"user_id"],
[user objectForKey:@"username"],
2012-12-11 17:53:17 -08:00
userEditButton,
userLikeButton,
2012-07-29 13:24:03 -07:00
[self getReplies:[commentDict objectForKey:@"replies"] forUserId:[commentDict objectForKey:@"user_id"]]];
2012-07-29 13:24:03 -07:00
}
2012-06-25 20:28:07 -07:00
return comment;
}
2012-07-20 15:54:10 -07:00
- (NSString *)getReplies:(NSArray *)replies forUserId:(NSString *)commentUserId {
NSString *repliesString = @"";
if (replies.count > 0) {
repliesString = [repliesString stringByAppendingString:@"<div class=\"NB-story-comment-replies\">"];
for (int i = 0; i < replies.count; i++) {
2012-07-20 15:54:10 -07:00
NSDictionary *replyDict = [replies objectAtIndex:i];
NSDictionary *user = [appDelegate getUser:[[replyDict objectForKey:@"user_id"] intValue]];
2012-07-20 15:54:10 -07:00
NSString *userEditButton = @"";
NSString *replyUserId = [NSString stringWithFormat:@"%@", [replyDict objectForKey:@"user_id"]];
NSString *replyId = [replyDict objectForKey:@"reply_id"];
NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]];
2012-07-20 15:54:10 -07:00
if ([replyUserId isEqualToString:currentUserId]) {
userEditButton = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-edit-button NB-story-comment-share-edit-button NB-button\">"
"<a href=\"http://ios.newsblur.com/edit-reply/%@/%@/%@\">"
2012-07-20 15:54:10 -07:00
"<div class=\"NB-story-comment-edit-button-wrapper\">"
2012-08-13 16:20:23 -07:00
"Edit"
2012-07-20 15:54:10 -07:00
"</div>"
"</a>"
2012-07-20 15:54:10 -07:00
"</div>",
commentUserId,
replyUserId,
replyId
2012-07-20 15:54:10 -07:00
];
}
NSString *replyContent = [self textToHtml:[replyDict objectForKey:@"comments"]];
2012-08-09 16:45:52 -07:00
NSString *locationHtml = @"";
NSString *location = [NSString stringWithFormat:@"%@", [user objectForKey:@"location"]];
if (location.length) {
locationHtml = [NSString stringWithFormat:@"<div class=\"NB-story-comment-location\">%@</div>", location];
}
2012-07-21 18:25:56 -07:00
2012-08-13 15:34:53 -07:00
NSString *reply;
if (!self.isPhoneOrCompact) {
2012-08-13 15:34:53 -07:00
reply = [NSString stringWithFormat:@
2012-12-11 17:53:17 -08:00
"<div class=\"NB-story-comment-reply\" id=\"NB-user-comment-%@\">"
" <a class=\"NB-show-profile\" href=\"http://ios.newsblur.com/show-profile/%@\">"
" <div class=\"NB-highlight\"></div>"
" <img class=\"NB-story-comment-reply-photo\" src=\"%@\" />"
" </a>"
" <div class=\"NB-story-comment-username NB-story-comment-reply-username\">%@</div>"
" <div class=\"NB-story-comment-date NB-story-comment-reply-date\">%@ ago</div>"
" <div class=\"NB-story-comment-reply-content\">%@</div>"
" %@" // location
" <div class=\"NB-button-wrapper\">"
" %@" // edit
" </div>"
"</div>",
[replyDict objectForKey:@"reply_id"],
[user objectForKey:@"user_id"],
[user objectForKey:@"photo_url"],
[user objectForKey:@"username"],
[replyDict objectForKey:@"publish_date"],
replyContent,
locationHtml,
userEditButton];
2012-08-13 15:34:53 -07:00
} else {
2012-08-13 16:20:23 -07:00
reply = [NSString stringWithFormat:@
"<div class=\"NB-story-comment-reply\" id=\"NB-user-comment-%@\">"
" <a class=\"NB-show-profile\" href=\"http://ios.newsblur.com/show-profile/%@\">"
" <div class=\"NB-highlight\"></div>"
2012-08-13 16:20:23 -07:00
" <img class=\"NB-story-comment-reply-photo\" src=\"%@\" />"
" </a>"
" <div class=\"NB-story-comment-username NB-story-comment-reply-username\">%@</div>"
" <div class=\"NB-story-comment-date NB-story-comment-reply-date\">%@ ago</div>"
" <div class=\"NB-story-comment-reply-content\">%@</div>"
2012-12-11 17:53:17 -08:00
" %@"
" <div class=\"NB-button-wrapper\">"
" %@" // edit
" </div>"
2012-08-13 16:20:23 -07:00
"</div>",
[replyDict objectForKey:@"reply_id"],
[user objectForKey:@"user_id"],
[user objectForKey:@"photo_url"],
[user objectForKey:@"username"],
[replyDict objectForKey:@"publish_date"],
replyContent,
2012-12-11 17:53:17 -08:00
locationHtml,
2012-08-13 16:20:23 -07:00
userEditButton];
2012-08-13 15:34:53 -07:00
}
repliesString = [repliesString stringByAppendingString:reply];
}
repliesString = [repliesString stringByAppendingString:@"</div>"];
}
return repliesString;
}
2016-08-10 13:26:52 -07:00
#pragma mark - Scrolling
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqual:@"contentOffset"]) {
BOOL isHorizontal = appDelegate.storyPagesViewController.isHorizontal;
BOOL isNavBarHidden = appDelegate.storyPagesViewController.isNavigationBarHidden;
if (self.webView.scrollView.contentOffset.y < (-1 * self.feedTitleGradient.frame.size.height + 1 + self.webView.scrollView.verticalScrollIndicatorInsets.top)) {
// Pulling
if (!pullingScrollview) {
pullingScrollview = YES;
for (id subview in self.webView.scrollView.subviews) {
UIImageView *imgView = [subview isKindOfClass:[UIImageView class]] ?
(UIImageView*)subview : nil;
// image views whose image is 1px wide are shadow images, hide them
if (imgView && imgView.image.size.width > 1) {
[self.webView.scrollView insertSubview:self.feedTitleGradient
belowSubview:subview];
[self.webView.scrollView bringSubviewToFront:subview];
}
}
}
} else {
// Normal reading
if (pullingScrollview) {
pullingScrollview = NO;
[self.feedTitleGradient.layer setShadowOpacity:0];
if (!isNavBarHidden) {
[self.webView insertSubview:self.feedTitleGradient aboveSubview:self.webView.scrollView];
self.feedTitleGradient.frame = CGRectMake(0, -1,
self.feedTitleGradient.frame.size.width,
self.feedTitleGradient.frame.size.height);
}
for (id subview in self.webView.scrollView.subviews) {
UIImageView *imgView = [subview isKindOfClass:[UIImageView class]] ?
(UIImageView*)subview : nil;
// image views whose image is 1px wide are shadow images, hide them
if (imgView && imgView.image.size.width == 1) {
imgView.hidden = NO;
}
}
}
}
if (appDelegate.storyPagesViewController.currentPage != self) return;
2013-03-07 10:55:23 -05:00
int webpageHeight = self.webView.scrollView.contentSize.height;
int viewportHeight = self.view.frame.size.height;
2013-03-07 10:55:23 -05:00
int topPosition = self.webView.scrollView.contentOffset.y;
CGFloat bottomInset = appDelegate.storyPagesViewController.view.safeAreaInsets.bottom;
int safeBottomMargin = -1 * bottomInset / 2;
2013-03-07 10:55:23 -05:00
int bottomPosition = webpageHeight - topPosition - viewportHeight;
BOOL singlePage = webpageHeight - 200 <= viewportHeight;
BOOL atBottom = bottomPosition < 150;
BOOL pullingDown = topPosition < 0;
2021-02-25 19:49:39 -08:00
BOOL atTop = topPosition < 50;
2019-01-16 19:15:07 -08:00
BOOL nearTop = topPosition < 100;
if (!hasScrolled && topPosition != 0) {
hasScrolled = YES;
}
if (!isNavBarHidden && self.canHideNavigationBar && !nearTop) {
// if (appDelegate.storyPagesViewController.autoscrollActive) {
[appDelegate.storyPagesViewController setNavigationBarHidden:YES];
}
if (isNavBarHidden && pullingDown) {
[appDelegate.storyPagesViewController setNavigationBarHidden:NO];
}
if (!atTop && !atBottom && !singlePage) {
BOOL traversalVisible = appDelegate.storyPagesViewController.traverseView.alpha > 0;
// Hide
[UIView animateWithDuration:.3 delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.appDelegate.storyPagesViewController.traverseView.alpha = 0;
if (traversalVisible) {
[self.appDelegate.storyPagesViewController hideAutoscrollImmediately];
}
} completion:^(BOOL finished) {
}];
} else if (singlePage || !isHorizontal) {
appDelegate.storyPagesViewController.traverseView.alpha = 1;
2018-09-26 09:56:56 -04:00
// NSLog(@" ---> Bottom position: %d", bottomPosition);
if (bottomPosition >= 0 || !isHorizontal) {
appDelegate.storyPagesViewController.traverseBottomConstraint.constant = safeBottomMargin + 20;
} else {
if (webpageHeight > 0) {
appDelegate.storyPagesViewController.traverseBottomConstraint.constant = viewportHeight - (webpageHeight - topPosition) + safeBottomMargin + 20;
} else {
appDelegate.storyPagesViewController.traverseBottomConstraint.constant = safeBottomMargin + 20;
}
}
2013-03-07 10:55:23 -05:00
} else if (!singlePage && (atTop && !atBottom)) {
// Pin to bottom of viewport, regardless of scrollview
appDelegate.storyPagesViewController.traversePinned = YES;
appDelegate.storyPagesViewController.traverseFloating = NO;
[appDelegate.storyPagesViewController.view layoutIfNeeded];
appDelegate.storyPagesViewController.traverseBottomConstraint.constant = safeBottomMargin + 20;
[UIView animateWithDuration:.3 delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[self.appDelegate.storyPagesViewController.view layoutIfNeeded];
self.appDelegate.storyPagesViewController.traverseView.alpha = 1;
} completion:nil];
} else if (appDelegate.storyPagesViewController.traverseView.alpha == 1 &&
appDelegate.storyPagesViewController.traversePinned) {
2013-03-07 10:55:23 -05:00
// Scroll with bottom of scrollview, but smoothly
appDelegate.storyPagesViewController.traverseFloating = YES;
[appDelegate.storyPagesViewController.view layoutIfNeeded];
appDelegate.storyPagesViewController.traverseBottomConstraint.constant = safeBottomMargin + 20;
2013-03-07 10:55:23 -05:00
[UIView animateWithDuration:.3 delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[self.appDelegate.storyPagesViewController.view layoutIfNeeded];
2013-03-07 10:55:23 -05:00
} completion:^(BOOL finished) {
self.appDelegate.storyPagesViewController.traversePinned = NO;
2013-03-07 10:55:23 -05:00
}];
} else {
// Scroll with bottom of scrollview
appDelegate.storyPagesViewController.traversePinned = NO;
appDelegate.storyPagesViewController.traverseFloating = YES;
appDelegate.storyPagesViewController.traverseView.alpha = 1;
appDelegate.storyPagesViewController.traverseBottomConstraint.constant = viewportHeight - (webpageHeight - topPosition) + safeBottomMargin + 20;
}
[self storeScrollPosition:YES];
}
}
- (NSInteger)scrollPosition {
NSInteger updatedPos = floor(self.webView.scrollView.contentOffset.y / self.webView.scrollView.contentSize.height
* 1000);
return updatedPos;
}
- (void)storeScrollPosition:(BOOL)queue {
__block NSInteger position = [self scrollPosition];
__block NSDictionary *story = self.activeStory;
__weak __typeof(&*self)weakSelf = self;
if (position < 0) return;
if (!hasScrolled) return;
NSString *storyIdentifier = [NSString stringWithFormat:@"markScrollPosition:%@", [story objectForKey:@"story_hash"]];
if (queue) {
NSTimeInterval interval = 2;
[JNWThrottledBlock runBlock:^{
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
if (!strongSelf) return;
NSInteger updatedPos = [strongSelf scrollPosition];
2020-08-27 15:08:46 -07:00
[self.appDelegate markScrollPosition:updatedPos inStory:story];
} withIdentifier:storyIdentifier throttle:interval];
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
(unsigned long)NULL), ^(void) {
2020-08-27 15:08:46 -07:00
[self.appDelegate markScrollPosition:position inStory:story];
});
}
}
- (void)scrollToLastPosition:(BOOL)animated {
if (hasScrolled) return;
hasScrolled = YES;
__block NSString *storyHash = [self.activeStory objectForKey:@"story_hash"];
__weak __typeof(&*self)weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
(unsigned long)NULL), ^(void) {
2020-08-27 15:08:46 -07:00
[self.appDelegate.database inDatabase:^(FMDatabase *db) {
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
if (!strongSelf) {
NSLog(@" !!! Lost strong reference to story detail vc");
return;
}
FMResultSet *cursor = [db executeQuery:@"SELECT scroll, story_hash FROM story_scrolls s WHERE s.story_hash = ? LIMIT 1", storyHash];
while ([cursor next]) {
NSDictionary *story = [cursor resultDictionary];
id scroll = [story objectForKey:@"scroll"];
2020-08-27 15:08:46 -07:00
if (([scroll isKindOfClass:[NSNull class]] || [scroll integerValue] == 0) && !self->scrollPct) {
NSLog(@" ---> No scroll found for story: %@", [strongSelf.activeStory objectForKey:@"story_title"]);
// No scroll found
continue;
}
dispatch_async(dispatch_get_main_queue(), ^{
2020-08-27 15:08:46 -07:00
if (!self->scrollPct) self->scrollPct = [scroll floatValue] / 1000.f;
NSInteger position = floor(self->scrollPct * strongSelf.webView.scrollView.contentSize.height);
NSInteger maxPosition = (NSInteger)(floor(strongSelf.webView.scrollView.contentSize.height - strongSelf.webView.frame.size.height));
if (position > maxPosition) {
NSLog(@"Position too far, scaling back to max position: %ld > %ld", (long)position, (long)maxPosition);
position = maxPosition;
}
if (position > 0) {
2020-08-27 15:08:46 -07:00
NSLog(@"Scrolling to %ld / %.1f%% (%.f+%.f) on %@-%@", (long)position, self->scrollPct*100, strongSelf.webView.scrollView.contentSize.height, strongSelf.webView.frame.size.height, [story objectForKey:@"story_hash"], [strongSelf.activeStory objectForKey:@"story_title"]);
[strongSelf.webView.scrollView setContentOffset:CGPointMake(0, position) animated:animated];
}
});
}
[cursor close];
}];
});
}
- (void)setActiveStoryAtIndex:(NSInteger)activeStoryIndex {
if (activeStoryIndex >= 0) {
self.activeStory = [[appDelegate.storiesCollection.activeFeedStories
objectAtIndex:activeStoryIndex] mutableCopy];
} else {
self.activeStory = [appDelegate.activeStory mutableCopy];
}
}
- (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 = @"";
NSString *feedId = [NSString stringWithFormat:@"%@", [self.activeStory
objectForKey:@"story_feed_id"]];
if ([urlComponents count] > 1) {
action = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:1]];
}
2013-03-04 17:15:50 -08:00
// NSLog(@"Tapped url: %@", url);
// HACK: Using ios.newsblur.com to intercept the javascript share, reply, and edit events.
// the pathComponents do not work correctly unless it is a correctly formed url
// Is there a better way? Someone show me the light
if ([[url host] isEqualToString: @"ios.newsblur.com"]){
// reset the active comment
appDelegate.activeComment = nil;
appDelegate.activeShareType = action;
2012-07-20 15:54:10 -07:00
if ([action isEqualToString:@"reply"] ||
[action isEqualToString:@"edit-reply"] ||
2012-07-26 13:54:45 -07:00
[action isEqualToString:@"edit-share"] ||
[action isEqualToString:@"like-comment"] ||
[action isEqualToString:@"unlike-comment"]) {
// search for the comment from friends comments and shares
NSArray *friendComments = [self.activeStory objectForKey:@"friend_comments"];
for (int i = 0; i < friendComments.count; i++) {
NSString *userId = [NSString stringWithFormat:@"%@",
[[friendComments objectAtIndex:i] objectForKey:@"user_id"]];
if([userId isEqualToString:[NSString stringWithFormat:@"%@",
[urlComponents objectAtIndex:2]]]){
appDelegate.activeComment = [friendComments objectAtIndex:i];
}
}
NSArray *friendShares = [self.activeStory objectForKey:@"friend_shares"];
for (int i = 0; i < friendShares.count; i++) {
NSString *userId = [NSString stringWithFormat:@"%@",
[[friendShares objectAtIndex:i] objectForKey:@"user_id"]];
if([userId isEqualToString:[NSString stringWithFormat:@"%@",
[urlComponents objectAtIndex:2]]]){
appDelegate.activeComment = [friendShares objectAtIndex:i];
}
}
if (appDelegate.activeComment == nil) {
NSArray *publicComments = [self.activeStory objectForKey:@"public_comments"];
for (int i = 0; i < publicComments.count; i++) {
NSString *userId = [NSString stringWithFormat:@"%@",
[[publicComments objectAtIndex:i] objectForKey:@"user_id"]];
if([userId isEqualToString:[NSString stringWithFormat:@"%@",
[urlComponents objectAtIndex:2]]]){
appDelegate.activeComment = [publicComments objectAtIndex:i];
}
2012-06-25 18:05:25 -07:00
}
}
2012-07-20 15:54:10 -07:00
if (appDelegate.activeComment == nil) {
NSLog(@"PROBLEM! the active comment was not found in friend or public comments");
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
2012-07-20 15:54:10 -07:00
if ([action isEqualToString:@"reply"]) {
[appDelegate showShareView:@"reply"
2012-07-20 15:54:10 -07:00
setUserId:[NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]
setUsername:[NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:3]]
setReplyId:nil];
2012-07-20 15:54:10 -07:00
} else if ([action isEqualToString:@"edit-reply"]) {
[appDelegate showShareView:@"edit-reply"
2012-07-20 15:54:10 -07:00
setUserId:[NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]]
setUsername:nil
setReplyId:[NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:4]]];
2012-07-20 15:54:10 -07:00
} else if ([action isEqualToString:@"edit-share"]) {
[appDelegate showShareView:@"edit-share"
2012-07-20 15:54:10 -07:00
setUserId:nil
setUsername:nil
setReplyId:nil];
2012-07-26 13:54:45 -07:00
} else if ([action isEqualToString:@"like-comment"]) {
[self toggleLikeComment:YES];
} else if ([action isEqualToString:@"unlike-comment"]) {
[self toggleLikeComment:NO];
2012-07-20 15:54:10 -07:00
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"togglechanges"]) {
if (self.activeStory[@"story_changes"] != nil) {
[self.activeStory removeObjectForKey:@"story_changes"];
[self drawStory];
} else {
[self fetchStoryChanges];
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"share"]) {
[self openShareDialog];
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]];
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"save"]) {
BOOL isSaved = [appDelegate.storiesCollection toggleStorySaved:self.activeStory];
if (isSaved) {
[self openUserTagsDialog:[[urlComponents objectAtIndex:3] intValue]
yCoordinate:[[urlComponents objectAtIndex:4] intValue]
width:[[urlComponents objectAtIndex:5] intValue]
height:[[urlComponents objectAtIndex:6] intValue]];
}
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]];
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"classify-author"]) {
NSString *author = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]];
[self.appDelegate toggleAuthorClassifier:author feedId:feedId];
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"classify-tag"]) {
NSString *tag = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]];
[self.appDelegate toggleTagClassifier:tag feedId:feedId];
decisionHandler(WKNavigationActionPolicyCancel);
return;
2017-11-15 17:11:37 -08:00
} else if ([action isEqualToString:@"premium"]) {
[self.appDelegate showPremiumDialog];
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"show-profile"] && [urlComponents count] > 6) {
appDelegate.activeUserProfileId = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]];
for (int i = 0; i < appDelegate.storiesCollection.activeFeedUserProfiles.count; i++) {
NSString *userId = [NSString stringWithFormat:@"%@", [[appDelegate.storiesCollection.activeFeedUserProfiles objectAtIndex:i] objectForKey:@"user_id"]];
if ([userId isEqualToString:appDelegate.activeUserProfileId]){
appDelegate.activeUserProfileName = [NSString stringWithFormat:@"%@", [[appDelegate.storiesCollection.activeFeedUserProfiles objectAtIndex:i] objectForKey:@"username"]];
break;
}
}
[self showUserProfile:[urlComponents objectAtIndex:2]
xCoordinate:[[urlComponents objectAtIndex:3] intValue]
yCoordinate:[[urlComponents objectAtIndex:4] intValue]
width:[[urlComponents objectAtIndex:5] intValue]
height:[[urlComponents objectAtIndex:6] intValue]];
decisionHandler(WKNavigationActionPolicyCancel);
return;
} else if ([action isEqualToString:@"notify-loaded"]) {
[self webViewNotifyLoaded];
decisionHandler(WKNavigationActionPolicyCancel);
return;
2012-06-22 13:15:31 -07:00
}
} else if ([url.host hasSuffix:@"itunes.apple.com"]) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
// NSLog(@"Link clicked, views: %@ = %@", appDelegate.navigationController.topViewController, appDelegate.masterContainerViewController.childViewControllers);
if (appDelegate.isPresentingActivities) {
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
[appDelegate showOriginalStory:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)showOriginalStory:(UIGestureRecognizer *)gesture {
NSURL *url = [NSURL URLWithString:[appDelegate.activeStory
objectForKey:@"story_permalink"]];
[appDelegate hidePopover];
if (!gesture || [gesture isKindOfClass:[UITapGestureRecognizer class]]) {
[appDelegate showOriginalStory:url];
return;
}
if ([gesture isKindOfClass:[UIPinchGestureRecognizer class]] &&
gesture.state == UIGestureRecognizerStateChanged &&
[gesture numberOfTouches] >= 2) {
CGPoint touch1 = [gesture locationOfTouch:0 inView:self.view];
CGPoint touch2 = [gesture locationOfTouch:1 inView:self.view];
CGPoint slope = CGPointMake(touch2.x-touch1.x, touch2.y-touch1.y);
CGFloat distance = sqrtf(slope.x*slope.x + slope.y*slope.y);
CGFloat scale = [(UIPinchGestureRecognizer *)gesture scale];
// NSLog(@"Gesture: %f - %f", [(UIPinchGestureRecognizer *)gesture scale], distance);
if ((distance < 150 && scale <= 1.5) ||
(distance < 500 && scale <= 1.2)) {
return;
}
[appDelegate showOriginalStory:url];
gesture.enabled = NO;
gesture.enabled = YES;
}
}
- (void)showUserProfile:(NSString *)userId xCoordinate:(int)x yCoordinate:(int)y width:(int)width height:(int)height {
CGRect frame = CGRectZero;
if (!self.isPhoneOrCompact) {
2012-07-25 17:34:45 -07:00
// only adjust for the bar if user is scrolling
if (appDelegate.storiesCollection.isRiverView ||
appDelegate.storiesCollection.isSocialView ||
appDelegate.storiesCollection.isSavedView ||
appDelegate.storiesCollection.isReadView) {
if (self.webView.scrollView.contentOffset.y == -20) {
y = y + 20;
2012-07-25 17:34:45 -07:00
}
} else {
if (self.webView.scrollView.contentOffset.y == -9) {
y = y + 9;
}
}
frame = CGRectMake(x, y, width, height);
}
[appDelegate showUserProfileModal:[NSValue valueWithCGRect:frame]];
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
if (!self.hasStory) // other Web page loads aren't visible
return;
// DOM should already be set up here
2014-05-15 18:06:56 -07:00
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
2013-11-23 13:03:14 -08:00
[self changeFontSize:[userPreferences stringForKey:@"story_font_size"]];
2014-05-15 18:06:56 -07:00
[self changeLineSpacing:[userPreferences stringForKey:@"story_line_spacing"]];
[self.webView evaluateJavaScript:@"document.body.style.webkitTouchCallout='none';" completionHandler:nil];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[self loadStory];
}
- (void)loadStory {
if (!self.fullStoryHTML)
return; // if we're loading anything other than a full story, the view will be hidden
[self.activityIndicator stopAnimating];
[self loadHTMLString:self.fullStoryHTML];
self.fullStoryHTML = nil;
self.hasStory = YES;
[MBProgressHUD hideHUDForView:self.view animated:YES];
if ([appDelegate.storiesCollection.activeFeedStories count] &&
self.activeStoryId) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .15 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
[self checkTryFeedStory];
});
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.webView.hidden = NO;
[self.webView setNeedsDisplay];
});
}
- (void)webViewNotifyLoaded {
[self scrollToLastPosition:YES];
}
- (void)checkTryFeedStory {
// see if it's a tryfeed for animation
2012-11-09 14:13:44 -08:00
if (!self.webView.hidden &&
appDelegate.tryFeedCategory &&
([[self.activeStory objectForKey:@"id"] isEqualToString:appDelegate.tryFeedStoryId] ||
[[self.activeStory objectForKey:@"story_hash"] isEqualToString:appDelegate.tryFeedStoryId])) {
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.view animated:YES];
if ([appDelegate.tryFeedCategory isEqualToString:@"comment_like"] ||
[appDelegate.tryFeedCategory isEqualToString:@"comment_reply"]) {
NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]];
2012-08-01 12:41:02 -07:00
NSString *jsFlashString = [[NSString alloc] initWithFormat:@"slideToComment('%@', true, true);", currentUserId];
[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 evaluateJavaScript:jsFlashString completionHandler:nil];
}
appDelegate.tryFeedCategory = nil;
}
}
- (void)setFontStyle:(NSString *)fontStyle {
NSString *jsString;
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
[userPreferences setObject:fontStyle forKey:@"fontStyle"];
[userPreferences synchronize];
jsString = [NSString stringWithFormat:@
"document.getElementById('NB-font-style').setAttribute('class', '%@')",
fontStyle];
[self.webView evaluateJavaScript:jsString completionHandler:nil];
if (![fontStyle hasPrefix:@"NB-"]) {
jsString = [NSString stringWithFormat:@
"document.getElementById('NB-font-style').setAttribute('style', 'font-family: %@;')",
fontStyle];
} else {
jsString = @"document.getElementById('NB-font-style').setAttribute('style', '')";
}
[self.webView evaluateJavaScript:jsString completionHandler:nil];
}
- (void)changeFontSize:(NSString *)fontSize {
2013-11-23 13:03:14 -08:00
NSString *jsString = [[NSString alloc] initWithFormat:@"document.getElementById('NB-font-size').setAttribute('class', 'NB-%@')",
fontSize];
[self.webView evaluateJavaScript:jsString completionHandler:nil];
}
2014-05-15 18:06:56 -07:00
- (void)changeLineSpacing:(NSString *)lineSpacing {
NSString *jsString = [[NSString alloc] initWithFormat:@"document.getElementById('NB-line-spacing').setAttribute('class', 'NB-line-spacing-%@')",
lineSpacing];
[self.webView evaluateJavaScript:jsString completionHandler:nil];
2014-05-15 18:06:56 -07:00
}
2018-10-22 15:14:12 -07:00
- (void)updateStoryTheme {
self.view.backgroundColor = UIColorFromLightDarkRGB(0x707070, 0x404040);
2018-10-22 15:14:12 -07:00
NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-theme-style').href='storyDetailView%@.css';",
[ThemeManager themeManager].themeCSSSuffix];
[self.webView evaluateJavaScript:jsString completionHandler:nil];
self.webView.backgroundColor = UIColorFromLightDarkRGB(0x707070, 0x404040);
if ([ThemeManager themeManager].isDarkTheme) {
self.webView.scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
} else {
self.webView.scrollView.indicatorStyle = UIScrollViewIndicatorStyleBlack;
}
2018-10-22 15:14:12 -07:00
}
- (BOOL)canHideNavigationBar {
if (!appDelegate.storyPagesViewController.allowFullscreen) {
2019-01-16 19:15:07 -08:00
NSLog(@"canHideNavigationBar: no, toggle is off"); // log
return NO;
2018-12-24 14:39:28 -08:00
}
return YES;
}
- (BOOL)isSinglePage {
NSInteger webpageHeight = self.webView.scrollView.contentSize.height;
NSInteger viewportHeight = self.view.frame.size.height;
return webpageHeight - 200 <= viewportHeight;
}
#pragma mark -
#pragma mark Actions
2012-07-26 13:54:45 -07:00
- (void)toggleLikeComment:(BOOL)likeComment {
[appDelegate.storyPagesViewController showShareHUD:@"Favoriting"];
2012-07-26 13:54:45 -07:00
NSString *urlString;
if (likeComment) {
urlString = [NSString stringWithFormat:@"%@/social/like_comment",
2016-01-21 22:11:37 -08:00
self.appDelegate.url];
2012-07-26 13:54:45 -07:00
} else {
urlString = [NSString stringWithFormat:@"%@/social/remove_like_comment",
2016-01-21 22:11:37 -08:00
self.appDelegate.url];
2012-07-26 13:54:45 -07:00
}
2017-03-19 18:15:36 -07:00
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:[self.activeStory objectForKey:@"id"] forKey:@"story_id"];
[params setObject:[self.activeStory objectForKey:@"story_feed_id"] forKey:@"story_feed_id"];
[params setObject:[appDelegate.activeComment objectForKey:@"user_id"] forKey:@"comment_user_id"];
2012-07-26 13:54:45 -07:00
[appDelegate POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
2017-03-19 18:15:36 -07:00
[self finishLikeComment:responseObject];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
[self requestFailed:error statusCode:httpResponse.statusCode];
2017-03-19 18:15:36 -07:00
}];
2012-07-26 13:54:45 -07:00
}
2017-03-19 18:15:36 -07:00
- (void)finishLikeComment:(NSDictionary *)results {
2012-07-26 13:54:45 -07:00
// add the comment into the activeStory dictionary
NSDictionary *newStory = [DataUtilities updateComment:results for:appDelegate];
2012-07-26 13:54:45 -07:00
// update the current story and the activeFeedStories
appDelegate.activeStory = newStory;
[self setActiveStoryAtIndex:-1];
2012-07-26 13:54:45 -07:00
NSMutableArray *newActiveFeedStories = [[NSMutableArray alloc] init];
for (int i = 0; i < appDelegate.storiesCollection.activeFeedStories.count; i++) {
NSDictionary *feedStory = [appDelegate.storiesCollection.activeFeedStories objectAtIndex:i];
NSString *storyId = [NSString stringWithFormat:@"%@", [feedStory objectForKey:@"story_hash"]];
NSString *currentStoryId = [NSString stringWithFormat:@"%@", [self.activeStory objectForKey:@"story_hash"]];
2012-07-26 13:54:45 -07:00
if ([storyId isEqualToString: currentStoryId]){
[newActiveFeedStories addObject:newStory];
} else {
[newActiveFeedStories addObject:[appDelegate.storiesCollection.activeFeedStories objectAtIndex:i]];
2012-07-26 13:54:45 -07:00
}
}
appDelegate.storiesCollection.activeFeedStories = [NSArray arrayWithArray:newActiveFeedStories];
2012-07-26 13:54:45 -07:00
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.view animated:NO];
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.currentPage.view animated:NO];
[self refreshComments:@"like"];
2012-07-26 13:54:45 -07:00
}
- (void)requestFailed:(NSError *)error statusCode:(NSInteger)statusCode {
2017-03-19 18:15:36 -07:00
NSLog(@"Error in story detail: %@", error);
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.view animated:NO];
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.currentPage.view animated:NO];
2017-04-06 11:45:43 -07:00
[self informError:error statusCode:statusCode];
2012-06-29 12:54:52 -07:00
}
- (void)openShareDialog {
// test to see if the user has commented
// search for the comment from friends comments
NSArray *friendComments = [self.activeStory objectForKey:@"friend_comments"];
NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]];
for (int i = 0; i < friendComments.count; i++) {
NSString *userId = [NSString stringWithFormat:@"%@",
[[friendComments objectAtIndex:i] objectForKey:@"user_id"]];
if([userId isEqualToString:currentUserId]){
appDelegate.activeComment = [friendComments objectAtIndex:i];
break;
} else {
appDelegate.activeComment = nil;
}
}
if (appDelegate.activeComment == nil) {
[appDelegate showShareView:@"share"
setUserId:nil
setUsername:nil
setReplyId:nil];
} else {
[appDelegate showShareView:@"edit-share"
setUserId:nil
setUsername:nil
setReplyId:nil];
}
}
- (void)openTrainingDialog:(int)x yCoordinate:(int)y width:(int)width height:(int)height {
CGRect frame = CGRectZero;
if (!self.isPhoneOrCompact) {
// only adjust for the bar if user is scrolling
if (appDelegate.storiesCollection.isRiverView ||
appDelegate.storiesCollection.isSocialView ||
appDelegate.storiesCollection.isSavedView ||
appDelegate.storiesCollection.isReadView) {
if (self.webView.scrollView.contentOffset.y == -20) {
y = y + 20;
}
} else {
if (self.webView.scrollView.contentOffset.y == -9) {
y = y + 9;
}
}
frame = CGRectMake(x, y, width, height);
}
// NSLog(@"Open trainer: %@ (%d/%d/%d/%d)", NSStringFromCGRect(frame), x, y, width, height);
[appDelegate openTrainStory:[NSValue valueWithCGRect:frame]];
}
- (void)openUserTagsDialog:(int)x yCoordinate:(int)y width:(int)width height:(int)height {
CGRect frame = CGRectZero;
// only adjust for the bar if user is scrolling
if (appDelegate.storiesCollection.isRiverView ||
appDelegate.storiesCollection.isSocialView ||
appDelegate.storiesCollection.isSavedView ||
appDelegate.storiesCollection.isReadView) {
if (self.webView.scrollView.contentOffset.y == -20) {
y = y + 20;
}
} else {
if (self.webView.scrollView.contentOffset.y == -9) {
y = y + 9;
}
}
frame = CGRectMake(x, y, width, height);
[appDelegate openUserTagsStory:[NSValue valueWithCGRect:frame]];
}
- (void)tapImage:(UIGestureRecognizer *)gestureRecognizer {
CGPoint pt = [self pointForGesture:gestureRecognizer];
if (pt.x == CGPointZero.x && pt.y == CGPointZero.y) return;
// NSLog(@"Tapped point: %@", NSStringFromCGPoint(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];
[gestureRecognizer setEnabled:NO];
[gestureRecognizer setEnabled:YES];
}
}];
2014-09-18 16:55:37 -07:00
}
- (void)tapAndHold:(NSNotification*)notification {
CGPoint pt = [self pointForEvent:notification];
if (pt.x == CGPointZero.x && pt.y == CGPointZero.y) return;
[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];
}
}];
2014-09-18 16:55:37 -07:00
}
- (void)showImageMenu:(CGPoint)pt {
2020-08-27 15:08:46 -07:00
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'title');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *title, NSError *error) {
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'alt');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *alt, NSError *error) {
[self.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;
2020-08-27 15:08:46 -07:00
self->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) {
2020-08-27 15:08:46 -07:00
[self.appDelegate showOriginalStory:self->activeLongPressUrl];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Copy image" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
2020-08-27 15:08:46 -07:00
[self fetchImage:self->activeLongPressUrl copy:YES save:NO];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Save to camera roll" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
2020-08-27 15:08:46 -07:00
[self fetchImage:self->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 = self.appDelegate.storyPagesViewController.view;
[self presentViewController:alert animated:YES completion:nil];
}];
}];
}];
2014-09-18 16:55:37 -07:00
}
- (void)showLinkContextMenu:(CGPoint)pt {
2020-08-27 15:08:46 -07:00
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"linkAt(%li, %li, 'href');", (long)pt.x,(long)pt.y] completionHandler:^(NSString *href, NSError *error) {
[self.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];
[self.appDelegate showSendTo:self.appDelegate.storyPagesViewController
sender:ptValue
withUrl:url
authorName:nil
text:nil
title:title
feedTitle:nil
images:nil];
}];
}];
}
- (CGPoint)pointForEvent:(NSNotification*)notification {
if (self != appDelegate.storyPagesViewController.currentPage) return CGPointZero;
if (!self.view.window) return CGPointZero;
CGPoint pt;
NSDictionary *coord = [notification object];
pt.x = [[coord objectForKey:@"x"] floatValue];
pt.y = [[coord objectForKey:@"y"] floatValue];
// convert point from window to view coordinate system
pt = [webView convertPoint:pt fromView:nil];
// convert point from view to HTML coordinate system
// CGPoint offset = [self.webView scrollOffset];
// 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;
}
- (CGPoint)pointForGesture:(UIGestureRecognizer *)gestureRecognizer {
if (self != appDelegate.storyPagesViewController.currentPage) return CGPointZero;
if (!self.view.window) return CGPointZero;
CGPoint pt = [gestureRecognizer locationInView:appDelegate.storyPagesViewController.currentPage.webView];
// convert point from view to HTML coordinate system
// CGPoint offset = [self.webView scrollOffset];
// 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;
}
- (void)fetchImage:(NSURL *)url copy:(BOOL)copy save:(BOOL)save {
[MBProgressHUD hideHUDForView:self.webView animated:YES];
[appDelegate.storyPagesViewController showShareHUD:copy ?
@"Copying..." : @"Saving..."];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager setResponseSerializer:[AFImageResponseSerializer serializer]];
[manager GET:url.absoluteString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
UIImage *image = responseObject;
if (copy) {
[UIPasteboard generalPasteboard].image = image;
[self flashCheckmarkHud:@"copied"];
2018-07-13 15:25:27 -04:00
} else if (save) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *changeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
changeRequest.creationDate = [NSDate date];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success) {
[self flashCheckmarkHud:@"saved"];
} else {
[MBProgressHUD hideHUDForView:self.webView animated:NO];
[self informError:error];
}
});
}];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[MBProgressHUD hideHUDForView:self.webView animated:YES];
[self informError:@"Could not fetch image"];
}];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if ([self respondsToSelector:action])
return self.noStoryMessage.hidden;
return [super canPerformAction:action withSender:sender];
}
# pragma mark -
# pragma mark Subscribing to blurblog
- (void)subscribeToBlurblog {
[appDelegate.storyPagesViewController showShareHUD:@"Following"];
NSString *urlString = [NSString stringWithFormat:@"%@/social/follow",
2016-01-21 22:11:37 -08:00
self.appDelegate.url];
2017-03-19 18:15:36 -07:00
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:[appDelegate.storiesCollection.activeFeed
objectForKey:@"user_id"]
forKey:@"user_id"];
[appDelegate POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
2017-03-19 18:15:36 -07:00
[self finishSubscribeToBlurblog:responseObject];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
[self requestFailed:error statusCode:httpResponse.statusCode];
2017-03-19 18:15:36 -07:00
}];
}
2017-03-19 18:15:36 -07:00
- (void)finishSubscribeToBlurblog:(NSDictionary *)results {
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.view animated:NO];
self.storyHUD = [MBProgressHUD showHUDAddedTo:self.webView animated:YES];
self.storyHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.png"]];
self.storyHUD.mode = MBProgressHUDModeCustomView;
self.storyHUD.removeFromSuperViewOnHide = YES;
self.storyHUD.labelText = @"Followed";
[self.storyHUD hide:YES afterDelay:1];
appDelegate.storyPagesViewController.navigationItem.leftBarButtonItem = nil;
[appDelegate reloadFeedsView:NO];
2012-08-07 09:57:21 -07:00
}
- (void)refreshComments:(NSString *)replyId {
2012-08-13 15:34:53 -07:00
NSString *shareBarString = [self getShareBar];
2012-08-07 11:27:00 -07:00
NSString *commentString = [self getComments];
2012-06-25 20:28:07 -07:00
NSString *jsString = [[NSString alloc] initWithFormat:@
2012-08-13 15:34:53 -07:00
"document.getElementById('NB-comments-wrapper').innerHTML = '%@';"
"document.getElementById('NB-share-bar-wrapper').innerHTML = '%@';",
2012-08-13 15:34:53 -07:00
commentString,
shareBarString];
NSString *shareType = appDelegate.activeShareType;
[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:@"%@",
2020-08-27 15:08:46 -07:00
[self.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 {
[MBProgressHUD hideHUDForView:self.webView animated:NO];
[MBProgressHUD hideHUDForView:appDelegate.storyPagesViewController.currentPage.view animated:NO];
self.storyHUD = [MBProgressHUD showHUDAddedTo:self.webView animated:YES];
self.storyHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.png"]];
self.storyHUD.mode = MBProgressHUDModeCustomView;
self.storyHUD.removeFromSuperViewOnHide = YES;
if ([messageType isEqualToString:@"reply"]) {
self.storyHUD.labelText = @"Replied";
} else if ([messageType isEqualToString:@"edit-reply"]) {
self.storyHUD.labelText = @"Edited Reply";
} else if ([messageType isEqualToString:@"edit-share"]) {
self.storyHUD.labelText = @"Edited Comment";
} else if ([messageType isEqualToString:@"share"]) {
self.storyHUD.labelText = @"Shared";
} else if ([messageType isEqualToString:@"like-comment"]) {
self.storyHUD.labelText = @"Favorited";
} else if ([messageType isEqualToString:@"unlike-comment"]) {
self.storyHUD.labelText = @"Unfavorited";
} else if ([messageType isEqualToString:@"saved"]) {
self.storyHUD.labelText = @"Saved";
} else if ([messageType isEqualToString:@"unsaved"]) {
self.storyHUD.labelText = @"No longer saved";
2012-12-19 15:37:36 -08:00
} else if ([messageType isEqualToString:@"unread"]) {
self.storyHUD.labelText = @"Unread";
} else if ([messageType isEqualToString:@"added"]) {
self.storyHUD.labelText = @"Added";
} else if ([messageType isEqualToString:@"copied"]) {
self.storyHUD.labelText = @"Copied";
} else if ([messageType isEqualToString:@"saved"]) {
self.storyHUD.labelText = @"Saved";
}
[self.storyHUD hide:YES afterDelay:1];
}
#pragma mark -
#pragma mark Scrolling
- (void)scrolltoComment {
NSString *currentUserId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]];
2012-07-30 15:50:52 -07:00
NSString *jsFlashString = [[NSString alloc] initWithFormat:@"slideToComment('%@', true);", currentUserId];
[self.webView evaluateJavaScript:jsFlashString completionHandler:nil];
2012-06-25 20:28:07 -07:00
}
- (void)tryScrollingDown:(BOOL)down {
UIScrollView *scrollView = webView.scrollView;
CGPoint contentOffset = scrollView.contentOffset;
CGFloat frameHeight = scrollView.frame.size.height;
CGFloat scrollHeight = frameHeight - 45; // ~height of source bar and buttons
if (down) {
CGSize contentSize = scrollView.contentSize;
if (contentOffset.y + frameHeight == contentSize.height)
return;
contentOffset.y = MIN(contentOffset.y + scrollHeight, contentSize.height - frameHeight);
} else {
if (contentOffset.y <= 0)
return;
contentOffset.y = MAX(contentOffset.y - scrollHeight, 0);
}
[scrollView setContentOffset:contentOffset animated:YES];
}
- (void)scrollPageDown:(id)sender {
[self tryScrollingDown:YES];
}
- (void)scrollPageUp:(id)sender {
[self tryScrollingDown:NO];
}
- (NSString *)textToHtml:(NSString*)htmlString {
htmlString = [htmlString stringByReplacingOccurrencesOfString:@"'" withString:@"&#039;"];
htmlString = [htmlString stringByReplacingOccurrencesOfString:@"\n" withString:@"<br/>"];
return htmlString;
}
2012-11-07 17:54:16 -08:00
- (void)changeWebViewWidth {
// Don't do this in the background, to avoid scrolling to the top unnecessarily
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
return;
}
// [webView setNeedsLayout];
// [webView layoutIfNeeded];
// NSLog(@"changeWebViewWidth: %@ / %@ / %@", NSStringFromCGSize(self.view.bounds.size), NSStringFromCGSize(webView.scrollView.bounds.size), NSStringFromCGSize(webView.scrollView.contentSize));
NSInteger contentWidth = CGRectGetWidth(webView.scrollView.bounds);
2012-06-28 11:12:38 -07:00
NSString *contentWidthClass;
#if TARGET_OS_MACCATALYST
// CATALYST: probably will want to add custom CSS for Macs.
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-12-wide NB-width-768";
#else
UIInterfaceOrientation orientation = self.view.window.windowScene.interfaceOrientation;
if (UIInterfaceOrientationIsLandscape(orientation) && !self.isPhoneOrCompact) {
if (iPadPro12) {
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-12-wide";
} else if (iPadPro10) {
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-10-wide";
} else {
contentWidthClass = @"NB-ipad-wide";
}
} else if (!UIInterfaceOrientationIsLandscape(orientation) && !self.isPhoneOrCompact) {
if (iPadPro12) {
contentWidthClass = @"NB-ipad-narrow NB-ipad-pro-12-narrow";
} else if (iPadPro10) {
contentWidthClass = @"NB-ipad-narrow NB-ipad-pro-10-narrow";
} else {
contentWidthClass = @"NB-ipad-narrow";
}
} else if (UIInterfaceOrientationIsLandscape(orientation) && self.isPhoneOrCompact) {
contentWidthClass = @"NB-iphone-wide";
2012-06-28 11:12:38 -07:00
} else {
contentWidthClass = @"NB-iphone";
}
contentWidthClass = [NSString stringWithFormat:@"%@ NB-width-%d",
contentWidthClass, (int)floorf(CGRectGetWidth(webView.scrollView.bounds))];
#endif
NSString *alternateViewClass = @"";
if (!self.isPhoneOrCompact) {
if (appDelegate.detailViewController.storyTitlesOnLeft) {
alternateViewClass = @"NB-titles-bottom";
} else {
alternateViewClass = @"NB-titles-left";
}
}
NSString *riverClass = (appDelegate.storiesCollection.isRiverView ||
appDelegate.storiesCollection.isSocialView ||
appDelegate.storiesCollection.isSavedView ||
appDelegate.storiesCollection.isReadView) ?
@"NB-river" : @"NB-non-river";
2012-06-28 11:12:38 -07:00
NSString *jsString = [[NSString alloc] initWithFormat:
@"$('body').attr('class', '%@ %@ %@');"
"document.getElementById(\"viewport\").setAttribute(\"content\", \"width=%li;initial-scale=1; minimum-scale=1.0; maximum-scale=1.0; user-scalable=0;\");",
contentWidthClass,
alternateViewClass,
riverClass,
(long)contentWidth];
[self.webView evaluateJavaScript:jsString completionHandler:nil];
2012-11-08 17:39:32 -08:00
2012-11-09 14:57:33 -08:00
// self.webView.hidden = NO;
}
- (void)refreshHeader {
NSString *headerString = [[[self getHeader] stringByReplacingOccurrencesOfString:@"\'" withString:@"\\'"]
stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-header-container').innerHTML = '%@';",
headerString];
[self.webView evaluateJavaScript:jsString completionHandler:^(id result, NSError *error) {
[self.webView evaluateJavaScript:@"attachFastClick();" completionHandler:nil];
}];
}
- (void)refreshSideOptions {
NSString *sideOptionsString = [[[self getSideOptions] stringByReplacingOccurrencesOfString:@"\'" withString:@"\\'"]
stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-sideoptions-container').innerHTML = '%@';",
sideOptionsString];
[self.webView evaluateJavaScript:jsString completionHandler:^(id result, NSError *error) {
[self.webView evaluateJavaScript:@"attachFastClick();" completionHandler:nil];
}];
}
2013-05-28 18:07:19 -07:00
#pragma mark -
#pragma mark Text view
- (void)showTextOrStoryView {
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[self.activeStory objectForKey:@"story_feed_id"]];
if ([appDelegate isFeedInTextView:feedIdStr]) {
if (!self.inTextView) {
[self fetchTextView];
}
} else {
if (self.inTextView) {
[self showStoryView];
}
}
}
- (void)toggleTextView:(id)sender {
if (self.inTextView)
[self showStoryView];
else
[self fetchTextView];
}
- (void)showStoryView {
self.inTextView = NO;
[MBProgressHUD hideHUDForView:self.webView animated:YES];
[appDelegate.storyPagesViewController setTextButton:(StoryDetailViewController *)self];
[self drawStory];
}
2013-05-28 18:07:19 -07:00
- (void)fetchTextView {
if (!self.activeStoryId || !self.activeStory) return;
self.inTextView = YES;
2015-03-12 19:43:39 -07:00
// NSLog(@"Fetching Text: %@", [self.activeStory objectForKey:@"story_title"]);
if (self.activeStory == appDelegate.storyPagesViewController.currentPage.activeStory) {
[self.appDelegate.storyPagesViewController showFetchingTextNotifier];
}
2017-03-24 17:30:46 -07:00
NSString *storyId = [self.activeStory objectForKey:@"id"];
[appDelegate fetchTextForStory:[self.activeStory objectForKey:@"story_hash"] inFeed:[self.activeStory objectForKey:@"story_feed_id"] checkCache:YES withCallback:^(NSString *text) {
if (text != nil) {
[self finishFetchText:text storyId:storyId];
} else {
[self failedFetchText];
}
2017-03-24 17:30:46 -07:00
}];
2013-05-28 18:07:19 -07:00
}
- (void)failedFetchText {
[self.appDelegate.storyPagesViewController hideNotifier];
[MBProgressHUD hideHUDForView:self.webView animated:YES];
if (self.activeStory == appDelegate.storyPagesViewController.currentPage.activeStory) {
[self informError:@"Could not fetch text"];
}
self.inTextView = NO;
[appDelegate.storyPagesViewController setTextButton:(StoryDetailViewController *)self];
}
2013-05-28 18:07:19 -07:00
- (void)finishFetchText:(NSString *)text storyId:(NSString *)storyId {
2017-03-24 17:30:46 -07:00
if (![storyId isEqualToString:[self.activeStory objectForKey:@"id"]]) {
[self.appDelegate.storyPagesViewController hideNotifier];
2013-10-17 11:21:37 -07:00
[MBProgressHUD hideHUDForView:self.webView animated:YES];
self.inTextView = NO;
[appDelegate.storyPagesViewController setTextButton:(StoryDetailViewController *)self];
2013-10-17 11:21:37 -07:00
return;
}
NSMutableDictionary *newActiveStory = [self.activeStory mutableCopy];
[newActiveStory setObject:text forKey:@"original_text"];
if ([[self.activeStory objectForKey:@"story_hash"] isEqualToString:[appDelegate.activeStory objectForKey:@"story_hash"]]) {
appDelegate.activeStory = newActiveStory;
}
self.activeStory = newActiveStory;
2013-05-28 18:07:19 -07:00
[self.appDelegate.storyPagesViewController hideNotifier];
2013-05-28 18:07:19 -07:00
[MBProgressHUD hideHUDForView:self.webView animated:YES];
self.inTextView = YES;
[self drawStory];
2015-03-12 19:43:39 -07:00
// NSLog(@"Fetched Text: %@", [self.activeStory objectForKey:@"story_title"]);
2013-05-28 18:07:19 -07:00
}
- (void)fetchStoryChanges {
if (!self.activeStoryId || !self.activeStory) return;
self.inTextView = YES;
// NSLog(@"Fetching Changes: %@", [self.activeStory objectForKey:@"story_title"]);
if (self.activeStory == appDelegate.storyPagesViewController.currentPage.activeStory) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.appDelegate.storyPagesViewController showFetchingTextNotifier];
});
}
NSString *urlString = [NSString stringWithFormat:@"%@/rss_feeds/story_changes",
self.appDelegate.url];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:[self.activeStory objectForKey:@"story_hash"] forKey:@"story_hash"];
[params setObject:@"true" forKey:@"show_changes"];
NSString *storyId = [self.activeStory objectForKey:@"id"];
[appDelegate POST:urlString parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self finishFetchStoryChanges:responseObject storyId:storyId];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[self failedFetchStoryChanges:error];
}];
}
- (void)failedFetchStoryChanges:(NSError *)error {
[self.appDelegate.storyPagesViewController hideNotifier];
[MBProgressHUD hideHUDForView:self.webView animated:YES];
if (self.activeStory == appDelegate.storyPagesViewController.currentPage.activeStory) {
[self informError:@"Could not fetch changes"];
}
self.inTextView = NO;
[appDelegate.storyPagesViewController setTextButton:(StoryDetailViewController *)self];
}
- (void)finishFetchStoryChanges:(NSDictionary *)results storyId:(NSString *)storyId {
if ([results[@"failed"] boolValue]) {
[self failedFetchText];
return;
}
if (![storyId isEqualToString:self.activeStory[@"id"]]) {
[self.appDelegate.storyPagesViewController hideNotifier];
[MBProgressHUD hideHUDForView:self.webView animated:YES];
self.inTextView = NO;
[appDelegate.storyPagesViewController setTextButton:(StoryDetailViewController *)self];
return;
}
NSMutableDictionary *newActiveStory = [self.activeStory mutableCopy];
NSDictionary *resultsStory = results[@"story"];
newActiveStory[@"story_changes"] = resultsStory[@"story_content"];
if ([self.activeStory[@"story_hash"] isEqualToString:appDelegate.activeStory[@"story_hash"]]) {
appDelegate.activeStory = newActiveStory;
}
self.activeStory = newActiveStory;
[self.appDelegate.storyPagesViewController hideNotifier];
[MBProgressHUD hideHUDForView:self.webView animated:YES];
self.inTextView = YES;
[self drawStory];
// NSLog(@"Fetched Changes: %@", self.activeStory[@"story_title"]);
}
2013-05-28 18:07:19 -07:00
@end