From 4fab57a95be9df0c44cd5efa41e1fd8c51c051bc Mon Sep 17 00:00:00 2001 From: David Sinclair Date: Tue, 29 Oct 2024 20:08:13 -0700 Subject: [PATCH] #1899 (dashboard) - Added NewsBlur Dashboard to top of feeds list. - Updated StoryCache to cache stories for multiple feeds. --- clients/ios/Classes/AddSiteViewController.m | 1 + .../ios/Classes/DetailViewController.swift | 7 ++- clients/ios/Classes/FeedChooserItem.m | 2 +- .../Classes/FeedDetailObjCViewController.m | 46 +++++++++++-------- clients/ios/Classes/FeedsObjCViewController.h | 5 +- clients/ios/Classes/FeedsObjCViewController.m | 19 ++++++-- clients/ios/Classes/FolderTitleView.m | 16 +++++-- clients/ios/Classes/MoveSiteViewController.m | 1 + clients/ios/Classes/NewsBlurAppDelegate.m | 35 +++++++++++--- clients/ios/Classes/StoriesCollection.h | 1 + clients/ios/Classes/StoriesCollection.m | 6 +++ clients/ios/Classes/StoryCache.swift | 19 +++++++- .../Classes/StoryPagesObjCViewController.m | 3 ++ .../Share Extension/ShareViewController.swift | 2 +- 14 files changed, 124 insertions(+), 39 deletions(-) diff --git a/clients/ios/Classes/AddSiteViewController.m b/clients/ios/Classes/AddSiteViewController.m index 7ffb83bf8b..602a68656d 100644 --- a/clients/ios/Classes/AddSiteViewController.m +++ b/clients/ios/Classes/AddSiteViewController.m @@ -343,6 +343,7 @@ - (NSArray *)folders { @"widget_stories", @"river_blurblogs", @"river_global", + @"dashboard", @"infrequent", @"everything"]); } diff --git a/clients/ios/Classes/DetailViewController.swift b/clients/ios/Classes/DetailViewController.swift index 5b243912bc..f9dea01f37 100644 --- a/clients/ios/Classes/DetailViewController.swift +++ b/clients/ios/Classes/DetailViewController.swift @@ -113,7 +113,7 @@ class DetailViewController: BaseViewController { /// Whether or not using the list layout; see also the previous properties. @objc var storyTitlesInList: Bool { - return layout == .list + return layout == .list || storyTitlesInDashboard } /// Whether or not using the magazine layout; see also the previous properties. @@ -128,7 +128,7 @@ class DetailViewController: BaseViewController { /// Whether or not using the list, magazine, or grid layout; see also the previous properties. @objc var storyTitlesInGridView: Bool { - return [.list, .magazine, .grid].contains(layout) + return [.list, .magazine, .grid].contains(layout) || storyTitlesInDashboard } /// Whether or not using the legacy list for non-grid layout. @@ -136,6 +136,9 @@ class DetailViewController: BaseViewController { return !storyTitlesInGridView && style != .experimental } + /// Whether or not showing the dashboard. + @objc var storyTitlesInDashboard = false + /// Preference values. enum StyleValue { static let standard = "standard" diff --git a/clients/ios/Classes/FeedChooserItem.m b/clients/ios/Classes/FeedChooserItem.m index 9b4e24645b..c4c9173918 100644 --- a/clients/ios/Classes/FeedChooserItem.m +++ b/clients/ios/Classes/FeedChooserItem.m @@ -48,7 +48,7 @@ - (NSString *)identifierString { - (NSString *)title { NSString *title = self.info[@"feed_title"]; - if ([title isEqualToString:@" "] || [title isEqualToString:@"everything"] || [title isEqualToString:@"infrequent"]) { + if ([title isEqualToString:@" "] || [title isEqualToString:@"dashboard"] || [title isEqualToString:@"everything"] || [title isEqualToString:@"infrequent"]) { return @""; } else { return title; diff --git a/clients/ios/Classes/FeedDetailObjCViewController.m b/clients/ios/Classes/FeedDetailObjCViewController.m index 3360cab478..e21b3e09a1 100644 --- a/clients/ios/Classes/FeedDetailObjCViewController.m +++ b/clients/ios/Classes/FeedDetailObjCViewController.m @@ -1098,6 +1098,11 @@ - (void)fetchRiverPage:(int)page withCallback:(void(^)(void))callback { if (self.pageFetching || self.pageFinished) return; // NSLog(@"Fetching River in storiesCollection (pg. %ld): %@", (long)page, storiesCollection); + if ([storiesCollection.activeFolder isEqualToString:@"dashboard"]) { + NSLog(@"⚠️ Called fetchRiverPage with dashboard; this should never occur"); // log + return; + } + [self loadingFeed]; storiesCollection.feedPage = page; @@ -1861,6 +1866,8 @@ - (void)setTitleForBackButton { feedTitle = @"All Shared Stories"; } else if ([storiesCollection.activeFolder isEqualToString:@"river_global"]) { feedTitle = @"Global Shared Stories"; + } else if ([storiesCollection.activeFolder isEqualToString:@"dashboard"]) { + feedTitle = @"NewsBlur Dashboard"; } else if ([storiesCollection.activeFolder isEqualToString:@"everything"]) { feedTitle = @"All Stories"; } else if ([storiesCollection.activeFolder isEqualToString:@"infrequent"]) { @@ -2413,7 +2420,7 @@ - (void)markFeedsReadFromTimestamp:(NSInteger)cutoffTimestamp andOlder:(BOOL)old NSMutableDictionary *params = [NSMutableDictionary dictionary]; if (storiesCollection.isRiverView) { - if ([storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]) { + if ([storiesCollection.activeFolder isEqual:@"dashboard"] || [storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]) { for (NSString *folderName in appDelegate.dictFoldersArray) { for (id feedId in [appDelegate.dictFolders objectForKey:folderName]) { if (![feedId isKindOfClass:[NSString class]] || ![feedId startsWith:@"saved:"]) { @@ -2551,6 +2558,7 @@ - (IBAction)doOpenSettingsMenu:(id)sender { MenuViewController *viewController = [MenuViewController new]; __weak MenuViewController *weakViewController = viewController; + BOOL dashboard = appDelegate.storiesCollection.isDashboard; BOOL everything = appDelegate.storiesCollection.isEverything; BOOL infrequent = appDelegate.storiesCollection.isInfrequent; BOOL river = [self isRiver]; @@ -2571,7 +2579,7 @@ - (IBAction)doOpenSettingsMenu:(id)sender { } } - if ((!everything || !appDelegate.storiesCollection.isRiverView) && !infrequent && !saved && !read && !social && !widget) { + if ((!dashboard || !everything || !appDelegate.storiesCollection.isRiverView) && !infrequent && !saved && !read && !social && !widget) { NSString *manageText = [NSString stringWithFormat:@"Manage this %@…", appDelegate.storiesCollection.isRiverView ? @"folder" : @"site"]; [viewController addTitle:manageText iconName:@"menu_icn_move.png" selectionShouldDismiss:NO handler:^{ @@ -2579,7 +2587,7 @@ - (IBAction)doOpenSettingsMenu:(id)sender { }]; } - if (!appDelegate.storiesCollection.isRiverView && !infrequent && !saved && !read && !social && !widget) { + if (!appDelegate.storiesCollection.isRiverView && !dashboard && !infrequent && !saved && !read && !social && !widget) { [viewController addTitle:@"Train this site" iconName:@"menu_icn_train.png" selectionShouldDismiss:YES handler:^{ [self openTrainSite]; }]; @@ -2592,19 +2600,21 @@ - (IBAction)doOpenSettingsMenu:(id)sender { }]; } - [viewController addTitle:@"Notifications" iconName:@"dialog-notifications" iconColor:UIColorFromRGB(0xD58B4F) selectionShouldDismiss:YES handler:^{ - [self - openNotificationsWithFeed:[NSString stringWithFormat:@"%@", [self.appDelegate.storiesCollection.activeFeed objectForKey:@"id"]]]; - }]; - - [viewController addTitle:@"Statistics" iconName:@"menu_icn_statistics.png" selectionShouldDismiss:YES handler:^{ - [self - openStatisticsWithFeed:[NSString stringWithFormat:@"%@", [self.appDelegate.storiesCollection.activeFeed objectForKey:@"id"]]]; - }]; - - [viewController addTitle:@"Insta-fetch stories" iconName:@"menu_icn_fetch.png" selectionShouldDismiss:YES handler:^{ - [self instafetchFeed]; - }]; + if (!dashboard) { + [viewController addTitle:@"Notifications" iconName:@"dialog-notifications" iconColor:UIColorFromRGB(0xD58B4F) selectionShouldDismiss:YES handler:^{ + [self + openNotificationsWithFeed:[NSString stringWithFormat:@"%@", [self.appDelegate.storiesCollection.activeFeed objectForKey:@"id"]]]; + }]; + + [viewController addTitle:@"Statistics" iconName:@"menu_icn_statistics.png" selectionShouldDismiss:YES handler:^{ + [self + openStatisticsWithFeed:[NSString stringWithFormat:@"%@", [self.appDelegate.storiesCollection.activeFeed objectForKey:@"id"]]]; + }]; + + [viewController addTitle:@"Insta-fetch stories" iconName:@"menu_icn_fetch.png" selectionShouldDismiss:YES handler:^{ + [self instafetchFeed]; + }]; + } } NSString *preferenceKey = self.appDelegate.storiesCollection.markReadFilterKey; @@ -2625,7 +2635,7 @@ - (IBAction)doOpenSettingsMenu:(id)sender { [self reloadStories]; }]; - if (infrequent || !river) { + if (!dashboard || infrequent || !river) { [viewController addSegmentedControlWithTitles:@[@"All stories", @"Unread only"] selectIndex:[appDelegate.storiesCollection.activeReadFilter isEqualToString:@"all"] ? 0 : 1 selectionShouldDismiss:YES handler:^(NSUInteger selectedIndex) { if (selectedIndex == 0) { [userPreferences setObject:@"all" forKey:self.appDelegate.storiesCollection.readFilterKey]; @@ -3080,7 +3090,7 @@ - (void)openMoveView:(UINavigationController *)menuNavigationController { if ([title isEqualToString:@"everything"]) { title = @"Top Level"; iconName = @"menu_icn_all.png"; - } else if ([title isEqualToString:@"infrequent"]) { + } else if ([title isEqualToString:@"dashboard"] || [title isEqualToString:@"infrequent"]) { continue; } else { NSArray *components = [title componentsSeparatedByString:@" ▸ "]; diff --git a/clients/ios/Classes/FeedsObjCViewController.h b/clients/ios/Classes/FeedsObjCViewController.h index 8c1747b111..c1618e6df2 100644 --- a/clients/ios/Classes/FeedsObjCViewController.h +++ b/clients/ios/Classes/FeedsObjCViewController.h @@ -17,8 +17,9 @@ // indices in appDelegate.dictFoldersArray and button tags // keep in sync with NewsBlurTopSectionNames static enum { - NewsBlurTopSectionInfrequentSiteStories = 0, - NewsBlurTopSectionAllStories = 1 + NewsBlurTopSectionDashboard = 0, + NewsBlurTopSectionInfrequentSiteStories = 1, + NewsBlurTopSectionAllStories = 2 } NewsBlurTopSection; @interface FeedsObjCViewController : BaseViewController diff --git a/clients/ios/Classes/FeedsObjCViewController.m b/clients/ios/Classes/FeedsObjCViewController.m index 079735c1d1..d463b36b85 100644 --- a/clients/ios/Classes/FeedsObjCViewController.m +++ b/clients/ios/Classes/FeedsObjCViewController.m @@ -100,8 +100,9 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil + (void)initialize { // keep in sync with NewsBlurTopSection - NewsBlurTopSectionNames = @[/* 0 */ @"infrequent", - /* 1 */ @"everything"]; + NewsBlurTopSectionNames = @[/* 0 */ @"dashboard", + /* 1 */ @"infrequent", + /* 2 */ @"everything"]; } - (void)viewDidLoad { @@ -1390,7 +1391,13 @@ - (void)settingsUpdateSpecifierDictionary:(NSMutableDictionary *)dictionary { [values addObject:@"everything"]; for (NSString *folder in self.appDelegate.dictFoldersArray) { - if ([folder hasPrefix:@"river_"] || [folder isEqualToString:@"everything"] || [folder isEqualToString:@"infrequent"] || [folder isEqualToString:@"widget"] || [folder isEqualToString:@"read_stories"] || [folder hasPrefix:@"saved_"]) { + if ([folder hasPrefix:@"river_"] || + [folder isEqualToString:@"dashboard"] || + [folder isEqualToString:@"everything"] || + [folder isEqualToString:@"infrequent"] || + [folder isEqualToString:@"widget"] || + [folder isEqualToString:@"read_stories"] || + [folder hasPrefix:@"saved_"]) { continue; } @@ -1912,7 +1919,9 @@ - (CGFloat)tableView:(UITableView *)tableView NSString *folderName = [appDelegate.dictFoldersArray objectAtIndex:section]; BOOL visibleFeeds = [[self.visibleFolders objectForKey:folderName] boolValue]; - if (!visibleFeeds && section != NewsBlurTopSectionInfrequentSiteStories && section != NewsBlurTopSectionAllStories && + if (!visibleFeeds && section != NewsBlurTopSectionDashboard && + section != NewsBlurTopSectionInfrequentSiteStories && + section != NewsBlurTopSectionAllStories && ![folderName isEqualToString:@"river_global"] && ![folderName isEqualToString:@"river_blurblogs"] && ![folderName isEqualToString:@"saved_searches"] && @@ -2223,7 +2232,7 @@ - (void)markFeedRead:(NSString *)feedId cutoffDays:(NSInteger)days { } - (void)markFeedsRead:(NSArray *)feedIds cutoffDays:(NSInteger)days { - if (feedIds.count == 1 && ([feedIds.firstObject isEqual:@"everything"] || [feedIds.firstObject isEqual:@"infrequent"])) { + if (feedIds.count == 1 && ([feedIds.firstObject isEqual:@"dashboard"] || [feedIds.firstObject isEqual:@"everything"] || [feedIds.firstObject isEqual:@"infrequent"])) { [self markEverythingReadWithDays:days infrequent:[feedIds.firstObject isEqual:@"infrequent"]]; return; } diff --git a/clients/ios/Classes/FolderTitleView.m b/clients/ios/Classes/FolderTitleView.m index a33be9291d..8ac6e2f477 100644 --- a/clients/ios/Classes/FolderTitleView.m +++ b/clients/ios/Classes/FolderTitleView.m @@ -107,7 +107,9 @@ - (void) drawRect:(CGRect)rect { UIFont *font = [UIFont fontWithName:@"WhitneySSm-Medium" size:boldFontDescriptor.pointSize]; NSInteger titleOffsetY = ((rect.size.height - font.pointSize) / 2) - 1; NSString *folderTitle; - if (section == NewsBlurTopSectionInfrequentSiteStories) { + if (section == NewsBlurTopSectionDashboard) { + folderTitle = @"NewsBlur Dashboard"; + } else if (section == NewsBlurTopSectionInfrequentSiteStories) { folderTitle = @"Infrequent Site Stories"; } else if (section == NewsBlurTopSectionAllStories) { folderTitle = @"All Site Stories"; @@ -179,7 +181,7 @@ - (void) drawRect:(CGRect)rect { disclosureButton.frame = CGRectMake(customView.frame.size.width - 32, CGRectGetMidY(rect)-disclosureHeight/2-1, disclosureHeight, disclosureHeight); // Add collapse button to all folders except Everything - if (section != NewsBlurTopSectionInfrequentSiteStories && section != NewsBlurTopSectionAllStories && ![folderName isEqual:@"read_stories"] && ![folderName isEqual:@"river_global"] && ![folderName isEqual:@"widget_stories"]) { + if (section != NewsBlurTopSectionDashboard && section != NewsBlurTopSectionInfrequentSiteStories && section != NewsBlurTopSectionAllStories && ![folderName isEqual:@"read_stories"] && ![folderName isEqual:@"river_global"] && ![folderName isEqual:@"widget_stories"]) { if (!isFolderCollapsed) { UIImage *disclosureImage = [UIImage imageNamed:@"disclosure_down.png"]; [disclosureButton setImage:disclosureImage forState:UIControlStateNormal]; @@ -211,7 +213,15 @@ - (void) drawRect:(CGRect)rect { int width = 20; int height = 20; - if (section == NewsBlurTopSectionInfrequentSiteStories) { + if (section == NewsBlurTopSectionDashboard) { + folderImage = [UIImage imageNamed:@"saved-stories"]; + if (!appDelegate.isPhone) { + folderImageViewX = 10; + } else { + folderImageViewX = 7; + } + allowLongPress = YES; + } else if (section == NewsBlurTopSectionInfrequentSiteStories) { folderImage = [UIImage imageNamed:@"ak-icon-infrequent.png"]; if (!appDelegate.isPhone) { folderImageViewX = 10; diff --git a/clients/ios/Classes/MoveSiteViewController.m b/clients/ios/Classes/MoveSiteViewController.m index eac5548437..bbc9208a0c 100644 --- a/clients/ios/Classes/MoveSiteViewController.m +++ b/clients/ios/Classes/MoveSiteViewController.m @@ -212,6 +212,7 @@ - (NSArray *)pickerFolders { [self.folders addObject:@"— Top Level —"]; for (NSString *folder in appDelegate.dictFoldersArray) { + if ([folder isEqualToString:@"dashboard"]) continue; if ([folder isEqualToString:@"everything"]) continue; if ([folder isEqualToString:@"infrequent"]) continue; if ([folder isEqualToString:@"river_blurblogs"]) continue; diff --git a/clients/ios/Classes/NewsBlurAppDelegate.m b/clients/ios/Classes/NewsBlurAppDelegate.m index 4b4b44128c..eb7ba6bac7 100644 --- a/clients/ios/Classes/NewsBlurAppDelegate.m +++ b/clients/ios/Classes/NewsBlurAppDelegate.m @@ -1919,7 +1919,7 @@ - (NSArray *)allFeedIds { } - (NSArray *)feedIdsForFolderTitle:(NSString *)folderTitle { - if ([folderTitle isEqualToString:@"everything"] || [folderTitle isEqualToString:@"infrequent"]) { + if ([folderTitle isEqualToString:@"dashboard"] || [folderTitle isEqualToString:@"everything"] || [folderTitle isEqualToString:@"infrequent"]) { return @[folderTitle]; } else if ([folderTitle isEqualToString:@"widget_stories"]) { NSUserDefaults *groupDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.newsblur.NewsBlur-Group"]; @@ -2019,6 +2019,11 @@ - (void)loadRiverFeedDetailView:(FeedDetailViewController *)feedDetailView withF return; } + if ([folder isEqualToString:@"dashboard"]) { + NSLog(@"⚠️ Called loadRiverFeedDetailView with dashboard; this should never occur"); // log + return; + } + self.inFeedDetail = YES; [feedDetailView resetFeedDetail]; if (feedDetailView == feedDetailViewController) { @@ -2572,6 +2577,8 @@ - (void)donateFolder { activity.title = @"Read All Shared Stories"; } else if ([folder isEqualToString:@"river_global"]) { activity.title = @"Read Global Shared Stories"; + } else if ([folder isEqualToString:@"dashboard"]) { + activity.title = @"NewsBlur Dashboard"; } else if ([folder isEqualToString:@"everything"]) { activity.title = @"Read All the Stories"; } else if ([folder isEqualToString:@"infrequent"]) { @@ -2761,9 +2768,11 @@ - (NSInteger)unreadCountForFolder:(NSString *)folderName { } else if ([folderName isEqual:@"river_global"] || (!folderName && [storiesCollection.activeFolder isEqual:@"river_global"])) { total = 0; - } else if ([folderName isEqual:@"everything"] || + } else if ([folderName isEqual:@"dashboard"] || + [folderName isEqual:@"everything"] || [folderName isEqual:@"infrequent"] || - (!folderName && ([storiesCollection.activeFolder isEqual:@"everything"] || + (!folderName && ([storiesCollection.activeFolder isEqual:@"dashboard"] || + [storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]))) { // TODO: Fix race condition where self.dictUnreadCounts can be changed while being updated. for (id feedId in self.dictUnreadCounts) { @@ -2828,9 +2837,11 @@ - (UnreadCounts *)splitUnreadCountForFolder:(NSString *)folderName { } else if ([folderName isEqual:@"river_global"] || (!folderName && [storiesCollection.activeFolder isEqual:@"river_global"])) { // Nothing for global - } else if ([folderName isEqual:@"everything"] || + } else if ([folderName isEqual:@"dashboard"] || + [folderName isEqual:@"everything"] || [folderName isEqual:@"infrequent"] || - (!folderName && ([storiesCollection.activeFolder isEqual:@"everything"] || + (!folderName && ([storiesCollection.activeFolder isEqual:@"dashboard"] || + [storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]))) { NSMutableSet *uniqueFeeds = [NSMutableSet new]; for (NSArray *folder in [self.dictFolders allValues]) { @@ -2921,7 +2932,7 @@ - (NSDictionary *)markVisibleStoriesRead { #pragma mark Mark as read - (void)markActiveFolderAllRead { - if ([storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]) { + if ([storiesCollection.activeFolder isEqual:@"dashboard"] || [storiesCollection.activeFolder isEqual:@"everything"] || [storiesCollection.activeFolder isEqual:@"infrequent"]) { for (NSString *folderName in self.dictFoldersArray) { for (id feedId in [self.dictFolders objectForKey:folderName]) { [self markFeedAllRead:feedId]; @@ -3473,6 +3484,7 @@ - (BOOL)hasParentFolder:(NSString *)folderName { - (NSString *)extractParentFolderName:(NSString *)folderName { if ([folderName containsString:@"Top Level"] || + [folderName isEqual:@"dashboard"] || [folderName isEqual:@"everything"] || [folderName isEqual:@"infrequent"]) { folderName = @""; @@ -3491,6 +3503,7 @@ - (NSString *)extractParentFolderName:(NSString *)folderName { - (NSString *)extractFolderName:(NSString *)folderName { if ([folderName containsString:@"Top Level"] || + [folderName isEqual:@"dashboard"] || [folderName isEqual:@"everything"] || [folderName isEqual:@"infrequent"]) { folderName = @""; @@ -3739,6 +3752,9 @@ - (UIView *)makeFeedTitle:(NSDictionary *)feed { } else if (storiesCollection.isSocialRiverView && [storiesCollection.activeFolder isEqualToString:@"river_global"]) { titleLabel.text = [NSString stringWithFormat:@" Global Shared Stories"]; + } else if (storiesCollection.isRiverView && + [storiesCollection.activeFolder isEqualToString:@"dashboard"]) { + titleLabel.text = [NSString stringWithFormat:@" NewsBlur Dashboard"]; } else if (storiesCollection.isRiverView && [storiesCollection.activeFolder isEqualToString:@"everything"]) { titleLabel.text = [NSString stringWithFormat:@" All Site Stories"]; @@ -3786,6 +3802,9 @@ - (UIView *)makeFeedTitle:(NSDictionary *)feed { } else if (storiesCollection.isRiverView && [storiesCollection.activeFolder isEqualToString:@"everything"]) { titleImage = [UIImage imageNamed:@"all-stories"]; + } else if (storiesCollection.isRiverView && + [storiesCollection.activeFolder isEqualToString:@"dashboard"]) { + titleImage = [UIImage imageNamed:@"saved-stories"]; } else if (storiesCollection.isRiverView && [storiesCollection.activeFolder isEqualToString:@"infrequent"]) { titleImage = [UIImage imageNamed:@"ak-icon-infrequent.png"]; @@ -3816,6 +3835,8 @@ - (NSString *)folderTitle:(NSString *)folder { return @"All Shared Stories"; } else if ([folder isEqualToString:@"river_global"]) { return @"Global Shared Stories"; + } else if ([folder isEqualToString:@"dashboard"]) { + return @"NewsBlur Dashboard"; } else if ([folder isEqualToString:@"everything"]) { return @"All Site Stories"; } else if ([folder isEqualToString:@"infrequent"]) { @@ -3838,6 +3859,8 @@ - (UIImage *)folderIcon:(NSString *)folder { return [UIImage imageNamed:@"global-shares"]; } else if ([folder isEqualToString:@"river_blurblogs"]) { return [UIImage imageNamed:@"all-shares"]; + } else if ([folder isEqualToString:@"dashboard"]) { + return [UIImage imageNamed:@"saved-stories"]; } else if ([folder isEqualToString:@"everything"]) { return [UIImage imageNamed:@"all-stories"]; } else if ([folder isEqualToString:@"infrequent"]) { diff --git a/clients/ios/Classes/StoriesCollection.h b/clients/ios/Classes/StoriesCollection.h index 842b4ed005..7fd172cb6a 100644 --- a/clients/ios/Classes/StoriesCollection.h +++ b/clients/ios/Classes/StoriesCollection.h @@ -57,6 +57,7 @@ @property (nonatomic, readwrite) BOOL isWidgetView; @property (nonatomic, readwrite) BOOL showHiddenStories; @property (nonatomic, readwrite) BOOL inSearch; +@property (nonatomic, readonly) BOOL isDashboard; @property (nonatomic, readonly) BOOL isEverything; @property (nonatomic, readonly) BOOL isInfrequent; @property (nonatomic, readonly) BOOL isRiverOrSocial; diff --git a/clients/ios/Classes/StoriesCollection.m b/clients/ios/Classes/StoriesCollection.m index 35d6118af2..b539e7c793 100644 --- a/clients/ios/Classes/StoriesCollection.m +++ b/clients/ios/Classes/StoriesCollection.m @@ -87,6 +87,10 @@ - (void)transferStoriesFromCollection:(StoriesCollection *)fromCollection { self.savedSearchQuery = fromCollection.savedSearchQuery; } +- (BOOL)isDashboard { + return [activeFolder isEqualToString:@"dashboard"]; +} + - (BOOL)isEverything { return [activeFolder isEqualToString:@"everything"]; } @@ -361,6 +365,8 @@ - (NSString *)activeTitle { return @"Global Shared Stories"; } else if ([activeFolder isEqualToString:@"everything"]) { return @"All Site Stories"; + } else if ([activeFolder isEqualToString:@"dashboard"]) { + return @"NewsBlur Dashboard"; } else if ([activeFolder isEqualToString:@"infrequent"]) { return @"Infrequent Site Stories"; } else if (isSavedView && activeSavedStoryTag) { diff --git a/clients/ios/Classes/StoryCache.swift b/clients/ios/Classes/StoryCache.swift index 6dff320a6b..4f12735b70 100644 --- a/clients/ios/Classes/StoryCache.swift +++ b/clients/ios/Classes/StoryCache.swift @@ -22,7 +22,7 @@ import Foundation /// Using a list-style grid view layout for the story titles and story pages. var isList: Bool { - return appDelegate.detailViewController.layout == .list + return appDelegate.detailViewController.layout == .list || isDashboard } /// Using a magazine-style grid view layout for the story titles and story pages. @@ -40,6 +40,11 @@ import Foundation return appDelegate.detailViewController.storyTitlesInGridView } + /// Using the dashboard layout based on the grid view. + var isDashboard: Bool { + return appDelegate.detailViewController.storyTitlesInDashboard + } + var isPhone: Bool { return appDelegate.detailViewController.isPhone } @@ -52,6 +57,8 @@ import Foundation @Published var selected: Story? @Published var after = [Story]() + @Published var dashboard = [[Story]]() + var all: [Story] { if let selected { return before + [selected] + after @@ -127,4 +134,14 @@ import Foundation after[index] = Story(index: story.index) } } + + func reloadDashboard(for index: Int) { + if index == 0 { + dashboard.removeAll() + } + + reload() + + dashboard[index] = before + } } diff --git a/clients/ios/Classes/StoryPagesObjCViewController.m b/clients/ios/Classes/StoryPagesObjCViewController.m index 7c3aa7539d..73a48da3a2 100644 --- a/clients/ios/Classes/StoryPagesObjCViewController.m +++ b/clients/ios/Classes/StoryPagesObjCViewController.m @@ -286,6 +286,9 @@ - (void)viewWillAppear:(BOOL)animated { } else if (appDelegate.storiesCollection.isRiverView && [appDelegate.storiesCollection.activeFolder isEqualToString:@"everything"]) { titleImage = [UIImage imageNamed:@"all-stories"]; + } else if (appDelegate.storiesCollection.isRiverView && + [appDelegate.storiesCollection.activeFolder isEqualToString:@"dashboard"]) { + titleImage = [UIImage imageNamed:@"saved-stories"]; } else if (appDelegate.storiesCollection.isRiverView && [appDelegate.storiesCollection.activeFolder isEqualToString:@"infrequent"]) { titleImage = [UIImage imageNamed:@"ak-icon-infrequent.png"]; diff --git a/clients/ios/Share Extension/ShareViewController.swift b/clients/ios/Share Extension/ShareViewController.swift index cee5ae6ef2..cb2f46094f 100644 --- a/clients/ios/Share Extension/ShareViewController.swift +++ b/clients/ios/Share Extension/ShareViewController.swift @@ -99,7 +99,7 @@ class ShareViewController: UIViewController { if let foldersArray = prefs.object(forKey: "share:folders") as? [String] { folders = foldersArray - folders.removeAll { ["river_global", "river_blurblogs", "infrequent", "widget_stories", "read_stories", "saved_searches", "saved_stories"].contains($0) } + folders.removeAll { ["river_global", "river_blurblogs", "dashboard", "infrequent", "widget_stories", "read_stories", "saved_searches", "saved_stories"].contains($0) } } updateSaveButtonState()