From feaa1b8d986e05c8513dcdc700d6ef2bf8e7a848 Mon Sep 17 00:00:00 2001 From: Samuel Clay Date: Tue, 8 Oct 2013 19:33:11 -0700 Subject: [PATCH] Long press on feeds to choose mark as read days. Also works offline. Also fixing move feed dialog on iphone. --- .../ios/Classes/FeedDetailViewController.m | 5 +- clients/ios/Classes/MoveSiteViewController.m | 4 + clients/ios/Classes/NewsBlurAppDelegate.h | 3 +- clients/ios/Classes/NewsBlurAppDelegate.m | 80 +++++++++++++- clients/ios/Classes/NewsBlurViewController.h | 4 +- clients/ios/Classes/NewsBlurViewController.m | 102 +++++++++++++++--- .../ios/NewsBlur.xcodeproj/project.pbxproj | 6 +- clients/ios/NewsBlur_Prefix.pch | 2 +- .../MoveSiteViewController.xib | 18 ++-- 9 files changed, 187 insertions(+), 37 deletions(-) diff --git a/clients/ios/Classes/FeedDetailViewController.m b/clients/ios/Classes/FeedDetailViewController.m index 1e8ac9c56..8e474a6d6 100644 --- a/clients/ios/Classes/FeedDetailViewController.m +++ b/clients/ios/Classes/FeedDetailViewController.m @@ -1199,7 +1199,8 @@ - (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request { // [self informError:@"Failed to mark story as read"]; [appDelegate markStoriesRead:[request.userInfo objectForKey:@"stories"] - inFeeds:[request.userInfo objectForKey:@"feeds"]]; + inFeeds:[request.userInfo objectForKey:@"feeds"] + cutoffTimestamp:nil]; } - (void)finishMarkAllAsRead:(ASIFormDataRequest *)request { @@ -1312,7 +1313,7 @@ } else if (buttonIndex == 2) { [self instafetchFeed]; } - } + } } - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex { diff --git a/clients/ios/Classes/MoveSiteViewController.m b/clients/ios/Classes/MoveSiteViewController.m index e235db266..3c0e4a35e 100644 --- a/clients/ios/Classes/MoveSiteViewController.m +++ b/clients/ios/Classes/MoveSiteViewController.m @@ -48,6 +48,10 @@ [fromFolderInput setLeftViewMode:UITextFieldViewModeAlways]; navBar.tintColor = [UIColor colorWithRed:0.16f green:0.36f blue:0.46 alpha:0.9]; + + CGRect frame = self.navBar.frame; + frame.size.height += 20; + self.navBar.frame = frame; appDelegate = [NewsBlurAppDelegate sharedAppDelegate]; diff --git a/clients/ios/Classes/NewsBlurAppDelegate.h b/clients/ios/Classes/NewsBlurAppDelegate.h index 40a2843b3..7f6cddefc 100644 --- a/clients/ios/Classes/NewsBlurAppDelegate.h +++ b/clients/ios/Classes/NewsBlurAppDelegate.h @@ -336,7 +336,8 @@ - (void)markActiveFolderAllRead; - (void)markFeedAllRead:(id)feedId; - (void)markFeedReadInCache:(NSArray *)feedIds; -- (void)markStoriesRead:(NSDictionary *)stories inFeeds:(NSArray *)feeds; +- (void)markFeedReadInCache:(NSArray *)feedIds cutoffTimestamp:(NSInteger)cutoff; +- (void)markStoriesRead:(NSDictionary *)stories inFeeds:(NSArray *)feeds cutoffTimestamp:(NSInteger)cutoff; - (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request; - (void)finishMarkAllAsRead:(ASIHTTPRequest *)request; diff --git a/clients/ios/Classes/NewsBlurAppDelegate.m b/clients/ios/Classes/NewsBlurAppDelegate.m index 23787bc00..7f2450204 100644 --- a/clients/ios/Classes/NewsBlurAppDelegate.m +++ b/clients/ios/Classes/NewsBlurAppDelegate.m @@ -1792,7 +1792,70 @@ }); } -- (void)markStoriesRead:(NSDictionary *)stories inFeeds:(NSArray *)feeds { +- (void)markFeedReadInCache:(NSArray *)feedIds cutoffTimestamp:(NSInteger)cutoff { + for (NSString *feedId in feedIds) { + NSDictionary *unreadCounts = [self.dictUnreadCounts objectForKey:feedId]; + NSMutableDictionary *newUnreadCounts = [unreadCounts mutableCopy]; + NSMutableArray *stories = [NSMutableArray array]; + + [self.database inDatabase:^(FMDatabase *db) { + NSString *sql = [NSString stringWithFormat:@"SELECT * FROM stories s " + "INNER JOIN unread_hashes uh ON s.story_hash = uh.story_hash " + "WHERE s.story_feed_id = %@ AND s.story_timestamp < %ld", + feedId, (long)cutoff]; + FMResultSet *cursor = [db executeQuery:sql]; + + while ([cursor next]) { + NSDictionary *story = [cursor resultDictionary]; + [stories addObject:[NSJSONSerialization + JSONObjectWithData:[[story objectForKey:@"story_json"] + dataUsingEncoding:NSUTF8StringEncoding] + options:nil error:nil]]; + } + + [cursor close]; + }]; + + for (NSDictionary *story in stories) { + NSInteger score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]]; + if (score > 0) { + int unreads = MAX(0, [[newUnreadCounts objectForKey:@"ps"] intValue] - 1); + [newUnreadCounts setValue:[NSNumber numberWithInt:unreads] forKey:@"ps"]; + } else if (score == 0) { + int unreads = MAX(0, [[newUnreadCounts objectForKey:@"nt"] intValue] - 1); + [newUnreadCounts setValue:[NSNumber numberWithInt:unreads] forKey:@"nt"]; + } else if (score < 0) { + int unreads = MAX(0, [[newUnreadCounts objectForKey:@"ng"] intValue] - 1); + [newUnreadCounts setValue:[NSNumber numberWithInt:unreads] forKey:@"ng"]; + } + [self.dictUnreadCounts setObject:newUnreadCounts forKey:feedId]; + } + + [self.database inTransaction:^(FMDatabase *db, BOOL *rollback) { + for (NSDictionary *story in stories) { + NSMutableDictionary *newStory = [story mutableCopy]; + [newStory setObject:[NSNumber numberWithInt:1] forKey:@"read_status"]; + NSString *storyHash = [newStory objectForKey:@"story_hash"]; + [db executeUpdate:@"UPDATE stories SET story_json = ? WHERE story_hash = ?", + [newStory JSONRepresentation], + storyHash]; + } + NSString *deleteSql = [NSString + stringWithFormat:@"DELETE FROM unread_hashes " + "WHERE story_feed_id = \"%@\" " + "AND story_timestamp < %ld", + feedId, (long)cutoff]; + [db executeUpdate:deleteSql]; + [db executeUpdate:@"UPDATE unread_counts SET ps = ?, nt = ?, ng = ? WHERE feed_id = ?", + [newUnreadCounts objectForKey:@"ps"], + [newUnreadCounts objectForKey:@"nt"], + [newUnreadCounts objectForKey:@"ng"], + feedId]; + }]; + } +} + +- (void)markStoriesRead:(NSDictionary *)stories inFeeds:(NSArray *)feeds cutoffTimestamp:(NSInteger)cutoff { // Must be offline and marking all as read, so load all stories. if (stories && [[stories allKeys] count]) { @@ -1806,6 +1869,9 @@ NSString *sql = [NSString stringWithFormat:@"SELECT u.story_feed_id, u.story_hash " "FROM unread_hashes u WHERE u.story_feed_id IN (\"%@\")", [feeds componentsJoinedByString:@"\",\""]]; + if (cutoff) { + sql = [NSString stringWithFormat:@"%@ AND u.story_timestamp < %ld", sql, (long)cutoff]; + } FMResultSet *cursor = [db executeQuery:sql]; while ([cursor next]) { @@ -1824,10 +1890,14 @@ [cursor close]; }]; [self queueReadStories:feedsStories]; - for (NSString *feedId in [feedsStories allKeys]) { - [self markFeedAllRead:feedId]; + if (cutoff) { + [self markFeedReadInCache:[feedsStories allKeys] cutoffTimestamp:cutoff]; + } else { + for (NSString *feedId in [feedsStories allKeys]) { + [self markFeedAllRead:feedId]; + } + [self markFeedReadInCache:[feedsStories allKeys]]; } - [self markFeedReadInCache:[feedsStories allKeys]]; } } @@ -1836,7 +1906,7 @@ NSArray *feedIds = [request.userInfo objectForKey:@"feeds"]; NSDictionary *stories = [request.userInfo objectForKey:@"stories"]; - [self markStoriesRead:stories inFeeds:feedIds]; + [self markStoriesRead:stories inFeeds:feedIds cutoffTimestamp:nil]; } - (void)finishMarkAllAsRead:(ASIFormDataRequest *)request { diff --git a/clients/ios/Classes/NewsBlurViewController.h b/clients/ios/Classes/NewsBlurViewController.h index 3eacc9bcc..e522bb0cd 100644 --- a/clients/ios/Classes/NewsBlurViewController.h +++ b/clients/ios/Classes/NewsBlurViewController.h @@ -26,7 +26,9 @@ ASIHTTPRequestDelegate, NSCacheDelegate, WEPopoverControllerDelegate, UIPopoverControllerDelegate, IASKSettingsDelegate, -MCSwipeTableViewCellDelegate> { +MCSwipeTableViewCellDelegate, +UIGestureRecognizerDelegate, +UIActionSheetDelegate> { NewsBlurAppDelegate *appDelegate; NSMutableDictionary * activeFeedLocations; diff --git a/clients/ios/Classes/NewsBlurViewController.m b/clients/ios/Classes/NewsBlurViewController.m index 6a0bdebb2..7733b836f 100644 --- a/clients/ios/Classes/NewsBlurViewController.m +++ b/clients/ios/Classes/NewsBlurViewController.m @@ -133,6 +133,12 @@ static const CGFloat kFolderTitleHeight = 28.0f; appDelegate.activeClassifiers = [NSMutableDictionary dictionary]; + UILongPressGestureRecognizer *longpress = [[UILongPressGestureRecognizer alloc] + initWithTarget:self action:@selector(handleLongPress:)]; + longpress.minimumPressDuration = 1.0; + longpress.delegate = self; + [self.feedTitlesTable addGestureRecognizer:longpress]; + self.notifier = [[NBNotifier alloc] initWithTitle:@"Fetching stories..." inView:self.view withOffset:CGPointMake(0, self.feedViewToolbar.frame.size.height)]; @@ -156,7 +162,8 @@ static const CGFloat kFolderTitleHeight = 28.0f; self.viewShowingAllFeeds = NO; [self.intelligenceControl setSelectedSegmentIndex:1]; [appDelegate setSelectedIntelligence:0]; - } else { // default state, ALL BLURBLOG STORIES + } else { + // default state, ALL BLURBLOG STORIES self.viewShowingAllFeeds = YES; [self.intelligenceControl setSelectedSegmentIndex:0]; [appDelegate setSelectedIntelligence:0]; @@ -754,6 +761,52 @@ static const CGFloat kFolderTitleHeight = 28.0f; } } +- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer { + CGPoint p = [gestureRecognizer locationInView:self.feedTitlesTable]; + NSIndexPath *indexPath = [self.feedTitlesTable indexPathForRowAtPoint:p]; + + if (gestureRecognizer.state != UIGestureRecognizerStateBegan) return; + if (indexPath == nil) return; + + NSString *folderName = [appDelegate.dictFoldersArray objectAtIndex:indexPath.section]; + id feedId = [[appDelegate.dictFolders objectForKey:folderName] objectAtIndex:indexPath.row]; + NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId]; + BOOL isSocial = [appDelegate isSocialFeed:feedIdStr]; + NSDictionary *feed = isSocial ? + [appDelegate.dictSocialFeeds objectForKey:feedIdStr] : + [appDelegate.dictFeeds objectForKey:feedIdStr]; + + UIActionSheet *markReadSheet = [[UIActionSheet alloc] initWithTitle:[feed objectForKey:@"feed_title"] + delegate:self + cancelButtonTitle:@"Cancel" + destructiveButtonTitle:@"Mark site as read" + otherButtonTitles:@"1 day", @"3 days", @"7 days", @"14 days", nil]; + markReadSheet.accessibilityValue = feedIdStr; + [markReadSheet showInView:self.view]; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + NSString *feedId = actionSheet.accessibilityValue; + + switch (buttonIndex) { + case 0: + [self markFeedRead:feedId cutoffDays:0]; + break; + case 1: + [self markFeedRead:feedId cutoffDays:1]; + break; + case 2: + [self markFeedRead:feedId cutoffDays:3]; + break; + case 3: + [self markFeedRead:feedId cutoffDays:7]; + break; + case 4: + [self markFeedRead:feedId cutoffDays:14]; + break; + } +} + #pragma mark - #pragma mark Preferences @@ -1107,19 +1160,8 @@ heightForHeaderInSection:(NSInteger)section { } } else if (state == MCSwipeTableViewCellState3) { // Mark read - NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_feed_as_read", - NEWSBLUR_URL]; - NSURL *url = [NSURL URLWithString:urlString]; - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; - [request setPostValue:feedId forKey:@"feed_id"]; - [request setDidFinishSelector:@selector(finishMarkAllAsRead:)]; - [request setDidFailSelector:@selector(requestFailedMarkStoryRead:)]; - [request setUserInfo:@{@"feeds": @[feedId]}]; - [request setDelegate:self]; - [request startAsynchronous]; + [self markFeedRead:feedId cutoffDays:0]; - [appDelegate markFeedAllRead:feedId]; - [self.stillVisibleFeeds setObject:indexPath forKey:feedId]; [self.feedTitlesTable beginUpdates]; [self.feedTitlesTable reloadRowsAtIndexPaths:@[indexPath] @@ -1130,7 +1172,10 @@ heightForHeaderInSection:(NSInteger)section { - (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request { [appDelegate markStoriesRead:nil - inFeeds:[request.userInfo objectForKey:@"feeds"]]; + inFeeds:[request.userInfo objectForKey:@"feeds"] + cutoffTimestamp:[[request.userInfo objectForKey:@"cutoffTimestamp"] integerValue]]; + [self showOfflineNotifier]; + [self.feedTitlesTable reloadData]; } - (void)finishMarkAllAsRead:(ASIFormDataRequest *)request { @@ -1139,9 +1184,38 @@ heightForHeaderInSection:(NSInteger)section { return; } + if ([[request.userInfo objectForKey:@"cutoffTimestamp"] integerValue]) { + [self refreshFeedList:[[request.userInfo objectForKey:@"feeds"] objectAtIndex:0]]; + } [appDelegate markFeedReadInCache:[request.userInfo objectForKey:@"feeds"]]; } +- (void)markFeedRead:(NSString *)feedId cutoffDays:(NSInteger)days { + NSTimeInterval cutoffTimestamp = [[NSDate date] timeIntervalSince1970]; + cutoffTimestamp -= (days * 60*60*24); + + NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_feed_as_read", + NEWSBLUR_URL]; + NSURL *url = [NSURL URLWithString:urlString]; + ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; + [request setPostValue:feedId forKey:@"feed_id"]; + if (days) { + [request setPostValue:[NSNumber numberWithInteger:cutoffTimestamp] + forKey:@"cutoff_timestamp"]; + } + [request setDidFinishSelector:@selector(finishMarkAllAsRead:)]; + [request setDidFailSelector:@selector(requestFailedMarkStoryRead:)]; + [request setUserInfo:@{@"feeds": @[feedId], + @"cutoffTimestamp": [NSNumber numberWithInteger:cutoffTimestamp]}]; + [request setDelegate:self]; + [request startAsynchronous]; + + if (!days) { + [appDelegate markFeedAllRead:feedId]; + } else { + [self showRefreshNotifier]; + } +} #pragma mark - Table Actions diff --git a/clients/ios/NewsBlur.xcodeproj/project.pbxproj b/clients/ios/NewsBlur.xcodeproj/project.pbxproj index 211dc7d9b..9f04df064 100755 --- a/clients/ios/NewsBlur.xcodeproj/project.pbxproj +++ b/clients/ios/NewsBlur.xcodeproj/project.pbxproj @@ -2584,7 +2584,6 @@ ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -2607,7 +2606,7 @@ "-all_load", ); PRODUCT_NAME = NewsBlur; - PROVISIONING_PROFILE = "A0156932-124B-4F8E-8B93-EE6598D778F0"; + PROVISIONING_PROFILE = "EB97D956-BB90-4F2F-9919-F71949B04B3F"; TARGETED_DEVICE_FAMILY = "1,2"; "WARNING_CFLAGS[arch=*]" = "-Wall"; }; @@ -2620,7 +2619,6 @@ ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = NewsBlur_Prefix.pch; @@ -2640,7 +2638,7 @@ "-all_load", ); PRODUCT_NAME = NewsBlur; - PROVISIONING_PROFILE = "A0156932-124B-4F8E-8B93-EE6598D778F0"; + PROVISIONING_PROFILE = "EB97D956-BB90-4F2F-9919-F71949B04B3F"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; diff --git a/clients/ios/NewsBlur_Prefix.pch b/clients/ios/NewsBlur_Prefix.pch index 8c9950395..eca4c9f46 100644 --- a/clients/ios/NewsBlur_Prefix.pch +++ b/clients/ios/NewsBlur_Prefix.pch @@ -5,7 +5,7 @@ #import #import -//#define DEBUG 1 +#define DEBUG 1 #ifdef DEBUG #define BACKGROUND_REFRESH_SECONDS -5 diff --git a/clients/ios/Resources-iPhone/MoveSiteViewController.xib b/clients/ios/Resources-iPhone/MoveSiteViewController.xib index 19779a2df..fc1dadf7a 100644 --- a/clients/ios/Resources-iPhone/MoveSiteViewController.xib +++ b/clients/ios/Resources-iPhone/MoveSiteViewController.xib @@ -26,12 +26,12 @@ - + @@ -88,7 +88,7 @@ - + @@ -100,7 +100,7 @@ - +