diff --git a/WordPress/Classes/NotificationsViewController.m b/WordPress/Classes/NotificationsViewController.m index 9e17ee26181d..95172782eed5 100644 --- a/WordPress/Classes/NotificationsViewController.m +++ b/WordPress/Classes/NotificationsViewController.m @@ -16,9 +16,11 @@ #import "WPTableViewControllerSubclass.h" #import "NotificationSettingsViewController.h" #import "WPAccount.h" +#import "WPWebViewController.h" NSString * const NotificationsTableViewNoteCellIdentifier = @"NotificationsTableViewCell"; NSString * const NotificationsLastSyncDateKey = @"NotificationsLastSyncDate"; +NSString * const NotificationsJetpackInformationURL = @"http://jetpack.me/about/"; @interface NotificationsViewController () { BOOL _retrievingNotifications; @@ -45,7 +47,51 @@ - (id)init { - (NSString *)noResultsTitleText { - return NSLocalizedString(@"No notifications yet", @"Displayed when the user pulls up the notifications view and they have no items"); + if ([self showJetpackConnectMessage]) { + return NSLocalizedString(@"Connect to Jetpack", @"Displayed in the notifications view when a self-hosted user is not connected to Jetpack"); + } else { + return NSLocalizedString(@"No notifications yet", @"Displayed when the user pulls up the notifications view and they have no items"); + } +} +- (NSString *)noResultsMessageText +{ + if ([self showJetpackConnectMessage]) { + return NSLocalizedString(@"Jetpack supercharges your self‑hosted WordPress site.", @"Displayed in the notifications view when a self-hosted user is not connected to Jetpack"); + } else { + return nil; + } +} +- (NSString *)noResultsButtonText +{ + if ([self showJetpackConnectMessage]) { + return NSLocalizedString(@"Learn more", @""); + } else { + return nil; + } +} +- (UIView *)noResultsAccessoryView +{ + if ([self showJetpackConnectMessage]) { + return [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon-jetpack-gray"]]; + } else { + return nil; + } +} + +- (void)didTapNoResultsView:(WPNoResultsView *)noResultsView +{ + // Show Jetpack information screen + WPWebViewController *webViewController = [[WPWebViewController alloc] init]; + [webViewController setUrl:[NSURL URLWithString:NotificationsJetpackInformationURL]]; + [self.navigationController pushViewController:webViewController animated:YES]; +} + +- (BOOL)showJetpackConnectMessage +{ + // self.user == nil. No user implies that the user is using the + // app with a self-hosted blog not connected to jetpack + return self.user == nil; + } - (void)dealloc { @@ -235,6 +281,10 @@ - (void)configureCell:(NewNotificationsTableViewCell *)cell atIndexPath:(NSIndex cell.note = [self.resultsController objectAtIndexPath:indexPath]; } +- (BOOL)userCanRefresh { + return self.user != nil; +} + - (void)syncItemsViaUserInteractionWithSuccess:(void (^)())success failure:(void (^)(NSError *))failure { [self pruneOldNotes]; [self syncItemsWithSuccess:success failure:failure]; diff --git a/WordPress/Classes/WPNoResultsView.h b/WordPress/Classes/WPNoResultsView.h index e97ca0c19b62..26401a2ec57c 100644 --- a/WordPress/Classes/WPNoResultsView.h +++ b/WordPress/Classes/WPNoResultsView.h @@ -8,11 +8,20 @@ #import +@class WPNoResultsView; +@protocol WPNoResultsViewDelegate + +@optional +- (void)didTapNoResultsView:(WPNoResultsView *)noResultsView; +@end + @interface WPNoResultsView : UIView -+ (WPNoResultsView *)noResultsViewWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView; +@property (nonatomic, weak) id delegate; + ++ (WPNoResultsView *)noResultsViewWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView buttonTitle:(NSString *)buttonTitle; -- (void)setupWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView; +- (void)setupWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView buttonTitle:(NSString *)buttonTitle; - (void)showInView:(UIView *)view; - (void)centerInSuperview; diff --git a/WordPress/Classes/WPNoResultsView.m b/WordPress/Classes/WPNoResultsView.m index fe5c15e1c184..4adf20d1eea1 100644 --- a/WordPress/Classes/WPNoResultsView.m +++ b/WordPress/Classes/WPNoResultsView.m @@ -15,6 +15,7 @@ @interface WPNoResultsView () @property (nonatomic, copy) UILabel *titleLabel; @property (nonatomic, copy) UILabel *messageLabel; @property (nonatomic, copy) UIView *accessoryView; +@property (nonatomic, copy) UIButton *button; @end @implementation WPNoResultsView @@ -22,10 +23,10 @@ @implementation WPNoResultsView #pragma mark - #pragma mark Lifecycle Methods -+ (WPNoResultsView *)noResultsViewWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView { ++ (WPNoResultsView *)noResultsViewWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView buttonTitle:(NSString *)buttonTitle { WPNoResultsView *view = [[WPNoResultsView alloc] init]; - [view setupWithTitle:titleText message:messageText accessoryView:accessoryView]; + [view setupWithTitle:titleText message:messageText accessoryView:accessoryView buttonTitle:buttonTitle]; return view; } @@ -36,6 +37,7 @@ - (void)didMoveToSuperview { - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + self.delegate = nil; } - (void)layoutSubviews { @@ -51,9 +53,17 @@ - (void)layoutSubviews { CGSize messageSize = [_messageLabel.text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: _messageLabel.font} context:nil].size; _messageLabel.frame = CGRectMake(0, CGRectGetMaxY(_titleLabel.frame) + 10.0, width, messageSize.height); + [_button sizeToFit]; + CGSize buttonSize = _button.frame.size; + buttonSize.width += 20.0; + CGFloat buttonYOrigin = (CGRectGetHeight(_messageLabel.frame) > 0 ? CGRectGetMaxY(_messageLabel.frame) : CGRectGetMaxY(_titleLabel.frame)) + 20.0 ; + _button.frame = CGRectMake((width - buttonSize.width) / 2, buttonYOrigin, MIN(buttonSize.width, width), buttonSize.height); + CGRect bottomViewRect; - if (_messageLabel.text.length > 0) { + if (_button != nil) { + bottomViewRect = _button.frame; + } else if (_messageLabel.text.length > 0) { bottomViewRect = _messageLabel.frame; } else if (_titleLabel.text.length > 0) { bottomViewRect = _titleLabel.frame; @@ -72,7 +82,7 @@ - (void)layoutSubviews { #pragma mark Instance Methods -- (void)setupWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView { +- (void)setupWithTitle:(NSString *)titleText message:(NSString *)messageText accessoryView:(UIView *)accessoryView buttonTitle:(NSString *)buttonTitle { [self addSubview:accessoryView]; @@ -88,12 +98,39 @@ - (void)setupWithTitle:(NSString *)titleText message:(NSString *)messageText acc // Setup message text _messageLabel = [[UILabel alloc] init]; _messageLabel.font = [WPStyleGuide regularTextFont]; - _messageLabel.textColor = [WPStyleGuide whisperGrey]; + _messageLabel.textColor = [WPStyleGuide allTAllShadeGrey]; [self setMessageText:messageText]; _messageLabel.numberOfLines = 0; _messageLabel.textAlignment = NSTextAlignmentCenter; [self addSubview:_messageLabel]; + // Setup button + if (buttonTitle.length > 0) { + _button = [UIButton buttonWithType:UIButtonTypeCustom]; + [_button setTitle:buttonTitle forState:UIControlStateNormal]; + [_button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside]; + [_button setTitleColor:[WPStyleGuide allTAllShadeGrey] forState:UIControlStateNormal]; + [_button.titleLabel setFont:[WPStyleGuide regularTextFont]]; + + // Generate button background image + CGRect fillRect = CGRectMake(0, 0, 11.0, 36.0); + UIEdgeInsets capInsets = UIEdgeInsetsMake(4, 4, 4, 4); + UIImage *mainImage; + + UIGraphicsBeginImageContextWithOptions(fillRect.size, NO, [[UIScreen mainScreen] scale]); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetStrokeColorWithColor(context, [WPStyleGuide allTAllShadeGrey].CGColor); + CGContextAddPath(context, [UIBezierPath bezierPathWithRoundedRect:CGRectInset(fillRect, 1, 1) cornerRadius:2.0].CGPath); + CGContextStrokePath(context); + mainImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [_button setBackgroundImage:[mainImage resizableImageWithCapInsets:capInsets] forState:UIControlStateNormal]; + + [self addSubview:_button]; + } + + // Register for orientation changes [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; @@ -103,7 +140,7 @@ - (void)setupWithTitle:(NSString *)titleText message:(NSString *)messageText acc - (void)setTitleText:(NSString *)title { if (title.length > 0) { - _titleLabel.attributedText = [[NSAttributedString alloc] initWithString:title attributes:[WPNUXUtility titleAttributesWithColor:[WPStyleGuide whisperGrey]]]; + _titleLabel.attributedText = [[NSAttributedString alloc] initWithString:title attributes:[WPNUXUtility titleAttributesWithColor:[WPStyleGuide allTAllShadeGrey]]]; } [self setNeedsLayout]; } @@ -144,4 +181,11 @@ - (void)orientationDidChange:(NSNotification *)notification { [self setNeedsLayout]; } +- (void)buttonAction:(id)sender +{ + if ([self.delegate respondsToSelector:@selector(didTapNoResultsView:)]) { + [self.delegate didTapNoResultsView:self]; + } +} + @end diff --git a/WordPress/Classes/WPTableViewController.h b/WordPress/Classes/WPTableViewController.h index 07901ba67b8b..9b643d85658d 100644 --- a/WordPress/Classes/WPTableViewController.h +++ b/WordPress/Classes/WPTableViewController.h @@ -9,10 +9,11 @@ #import "Blog.h" #import "SettingsViewControllerDelegate.h" +#import "WPNoResultsView.h" extern CGFloat const WPTableViewTopMargin; -@interface WPTableViewController : UITableViewController +@interface WPTableViewController : UITableViewController @property (nonatomic, strong) Blog *blog; @property (nonatomic, readonly) BOOL isScrolling; @@ -21,6 +22,7 @@ extern CGFloat const WPTableViewTopMargin; - (void)promptForPassword; - (NSString *)noResultsTitleText; - (NSString *)noResultsMessageText; +- (NSString *)noResultsButtonText; - (UIView *)noResultsAccessoryView; - (void)configureNoResultsView; diff --git a/WordPress/Classes/WPTableViewController.m b/WordPress/Classes/WPTableViewController.m index 425dba81fabe..be8b66d0ee7f 100644 --- a/WordPress/Classes/WPTableViewController.m +++ b/WordPress/Classes/WPTableViewController.m @@ -168,6 +168,8 @@ - (void)setEditing:(BOOL)editing animated:(BOOL)animated { [super setEditing:editing animated:animated]; } +#pragma mark - No Results View + - (NSString *)noResultsTitleText { NSString *ttl = NSLocalizedString(@"No %@ yet", @"A string format. The '%@' will be replaced by the relevant type of object, posts, pages or comments."); @@ -185,6 +187,11 @@ - (UIView *)noResultsAccessoryView return nil; } +- (NSString *)noResultsButtonText +{ + return nil; +} + #pragma mark - Property accessors - (void)setBlog:(Blog *)blog { @@ -453,11 +460,20 @@ - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id #pragma mark - UIRefreshControl Methods - (void)refresh { + + if (![self userCanRefresh]) { + [self.refreshControl endRefreshing]; + return; + } + didTriggerRefresh = YES; [self syncItemsViaUserInteraction]; [noResultsView removeFromSuperview]; } +- (BOOL)userCanRefresh { + return YES; +} #pragma mark - UIScrollViewDelegate Methods @@ -566,7 +582,10 @@ - (void)configureNoResultsView { - (UIView *)createNoResultsView { - return [WPNoResultsView noResultsViewWithTitle:[self noResultsTitleText] message:[self noResultsMessageText] accessoryView:[self noResultsAccessoryView]]; + WPNoResultsView *view = [WPNoResultsView noResultsViewWithTitle:[self noResultsTitleText] message:[self noResultsMessageText] accessoryView:[self noResultsAccessoryView] buttonTitle:[self noResultsButtonText]]; + view.delegate = self; + + return view; } - (void)hideRefreshHeader { diff --git a/WordPress/Classes/WPWebViewController.m b/WordPress/Classes/WPWebViewController.m index 78598d74bba3..1557f39ba9e3 100644 --- a/WordPress/Classes/WPWebViewController.m +++ b/WordPress/Classes/WPWebViewController.m @@ -33,6 +33,10 @@ - (void)dealloc { _statusTimer = nil; } +- (BOOL)hidesBottomBarWhenPushed { + return YES; +} + - (NSString *)statsPrefixForShareActions { if (_statsPrefixForShareActions == nil) { return @"Webview"; diff --git a/WordPress/Resources/Images/icon-jetpack-gray.png b/WordPress/Resources/Images/icon-jetpack-gray.png new file mode 100644 index 000000000000..fa580d38c1cb Binary files /dev/null and b/WordPress/Resources/Images/icon-jetpack-gray.png differ diff --git a/WordPress/Resources/Images/icon-jetpack-gray@2x.png b/WordPress/Resources/Images/icon-jetpack-gray@2x.png new file mode 100644 index 000000000000..2cf917b4a71a Binary files /dev/null and b/WordPress/Resources/Images/icon-jetpack-gray@2x.png differ diff --git a/WordPress/Resources/WPWebViewController.xib b/WordPress/Resources/WPWebViewController.xib index fdb804602f54..86cf9f58a127 100644 --- a/WordPress/Resources/WPWebViewController.xib +++ b/WordPress/Resources/WPWebViewController.xib @@ -1,8 +1,8 @@ - + - - + + @@ -61,15 +61,15 @@ - + + - \ No newline at end of file diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 8a4af064bbc2..5c950fc3a8c4 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -951,6 +951,8 @@ E24085A1183FDDEC002EB0EF /* penandink@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E240859F183FDDEC002EB0EF /* penandink@2x.png */; }; E2586BD31832F31D004B5B67 /* icon-site-field@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E2586BD11832F31D004B5B67 /* icon-site-field@2x.png */; }; E2586BD41832F31D004B5B67 /* icon-site-field.png in Resources */ = {isa = PBXBuildFile; fileRef = E2586BD21832F31D004B5B67 /* icon-site-field.png */; }; + E2AA879D1850F91E00886693 /* icon-jetpack-gray.png in Resources */ = {isa = PBXBuildFile; fileRef = E2AA879B1850F91E00886693 /* icon-jetpack-gray.png */; }; + E2AA879E1850F91E00886693 /* icon-jetpack-gray@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E2AA879C1850F91E00886693 /* icon-jetpack-gray@2x.png */; }; EC4696FF0EA75D460040EE8E /* PagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EC4696FE0EA75D460040EE8E /* PagesViewController.m */; }; FD21397F13128C5300099582 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FD21397E13128C5300099582 /* libiconv.dylib */; }; FD22C2B716CD2B20002BA030 /* navbar_settings.png in Resources */ = {isa = PBXBuildFile; fileRef = FD22C2B516CD2B20002BA030 /* navbar_settings.png */; }; @@ -2130,6 +2132,8 @@ E240859F183FDDEC002EB0EF /* penandink@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "penandink@2x.png"; sourceTree = ""; }; E2586BD11832F31D004B5B67 /* icon-site-field@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-site-field@2x.png"; sourceTree = ""; }; E2586BD21832F31D004B5B67 /* icon-site-field.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-site-field.png"; sourceTree = ""; }; + E2AA879B1850F91E00886693 /* icon-jetpack-gray.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-jetpack-gray.png"; path = "Resources/Images/icon-jetpack-gray.png"; sourceTree = ""; }; + E2AA879C1850F91E00886693 /* icon-jetpack-gray@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-jetpack-gray@2x.png"; path = "Resources/Images/icon-jetpack-gray@2x.png"; sourceTree = ""; }; EBC24772E5CD4036B5AFD803 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = ../Pods/Pods.xcconfig; sourceTree = SOURCE_ROOT; }; EC4696FD0EA75D460040EE8E /* PagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PagesViewController.h; sourceTree = ""; }; EC4696FE0EA75D460040EE8E /* PagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PagesViewController.m; sourceTree = ""; }; @@ -3146,6 +3150,8 @@ 5D7BEA991774A10D00423E7F /* icon-check@2x.png */, E233414F182DB97000A4CC40 /* icon-jetpack.png */, E2334150182DB97000A4CC40 /* icon-jetpack@2x.png */, + E2AA879B1850F91E00886693 /* icon-jetpack-gray.png */, + E2AA879C1850F91E00886693 /* icon-jetpack-gray@2x.png */, E232E5FD182E9E8600792CA0 /* icon-username-field@2x.png */, E232E5FF182EA15A00792CA0 /* icon-username-field.png */, E232E601182EA16200792CA0 /* icon-password-field.png */, @@ -4501,12 +4507,14 @@ 46F8714B1838BB4E00BC149B /* reader-postaction-tag.png in Resources */, 30E9683D15AE4EB10047DD84 /* sidebar-logo@2x.png in Resources */, 30E9684115AE60B20047DD84 /* navbar_add.png in Resources */, + E2AA879D1850F91E00886693 /* icon-jetpack-gray.png in Resources */, E232E603182EA16200792CA0 /* icon-password-field.png in Resources */, 30E9684215AE60B20047DD84 /* navbar_add@2x.png in Resources */, 30E9685515AE713A0047DD84 /* navbar_primary_button_bg_landscape_active.png in Resources */, 30E9685615AE713A0047DD84 /* navbar_primary_button_bg_landscape_active@2x.png in Resources */, 30E9685715AE713A0047DD84 /* navbar_primary_button_bg_landscape.png in Resources */, 30E9685815AE713A0047DD84 /* navbar_primary_button_bg_landscape@2x.png in Resources */, + E2AA879E1850F91E00886693 /* icon-jetpack-gray@2x.png in Resources */, 3001AB4215AEE56C00A9F8F3 /* toolbar_swipe_trash@2x.png in Resources */, 3001AB4615AEF05500A9F8F3 /* toolbar_swipe_approve.png in Resources */, 3001AB4715AEF05500A9F8F3 /* toolbar_swipe_approve@2x.png in Resources */,