Commit 9492962a authored by adamta's avatar adamta Committed by Commit Bot

[iOS] Discover feed item in NTP

Adds the Discover feed coming from the Discover provider to the NTP
ContentSuggestions collection view. The feed is shown based on the
feature flag, as well as the "Article Suggestions" setting in the
settings menu. Will replace the Zine feed if the flag is active.

Bug: 1085419
Change-Id: Iad9e94af68ad74e2d9028152cf696c7cb69d733e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2230765Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Commit-Queue: Adam Trudeau-Arcaro <adamta@google.com>
Cr-Commit-Position: refs/heads/master@{#776532}
parent a92dc8d7
...@@ -78,6 +78,7 @@ source_set("content_suggestions") { ...@@ -78,6 +78,7 @@ source_set("content_suggestions") {
"//ios/chrome/common/ui/favicon", "//ios/chrome/common/ui/favicon",
"//ios/chrome/common/ui/util", "//ios/chrome/common/ui/util",
"//ios/public/provider/chrome/browser", "//ios/public/provider/chrome/browser",
"//ios/public/provider/chrome/browser/discover_feed",
"//ios/public/provider/chrome/browser/images", "//ios/public/provider/chrome/browser/images",
"//ios/public/provider/chrome/browser/signin", "//ios/public/provider/chrome/browser/signin",
"//ios/public/provider/chrome/browser/ui", "//ios/public/provider/chrome/browser/ui",
......
...@@ -33,6 +33,8 @@ source_set("cells_ui") { ...@@ -33,6 +33,8 @@ source_set("cells_ui") {
"content_suggestions_articles_header_item.mm", "content_suggestions_articles_header_item.mm",
"content_suggestions_cell.h", "content_suggestions_cell.h",
"content_suggestions_cell.mm", "content_suggestions_cell.mm",
"content_suggestions_discover_item.h",
"content_suggestions_discover_item.mm",
"content_suggestions_footer_item.h", "content_suggestions_footer_item.h",
"content_suggestions_footer_item.mm", "content_suggestions_footer_item.mm",
"content_suggestions_header_item.h", "content_suggestions_header_item.h",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_DISCOVER_ITEM_H_
#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_DISCOVER_ITEM_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
@class ContentSuggestionsDiscoverCell;
// Item displaying entire Discover feed collection view.
@interface ContentSuggestionsDiscoverItem
: CollectionViewItem <SuggestedContent>
// Contains the Discover feed coming from Discover provider.
@property(strong, nonatomic) UIViewController* discoverFeed;
@end
// Associated cell, displaying the Discover feed adjusted to prevent nested
// scrolling.
@interface ContentSuggestionsDiscoverCell : MDCCollectionViewCell
// Height of Discover feed content to stretch the containing cell, in order to
// avoid nested scrolling.
- (CGFloat)feedHeight;
// Sets the cell content as the Discover feed's view.
- (void)setDiscoverFeedView:(UIViewController*)discoverFeed;
@end
#endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_DISCOVER_ITEM_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_discover_item.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#pragma mark - ContentSuggestionsDiscoverItem
@interface ContentSuggestionsDiscoverItem ()
// Contains a reference to the last configured cell containing the feed.
@property(strong, nonatomic) ContentSuggestionsDiscoverCell* lastConfiguredCell;
@end
@implementation ContentSuggestionsDiscoverItem
@synthesize suggestionIdentifier;
@synthesize metricsRecorded;
- (instancetype)initWithType:(NSInteger)type {
self = [super initWithType:type];
if (self) {
self.cellClass = [ContentSuggestionsDiscoverCell class];
}
return self;
}
- (void)configureCell:(ContentSuggestionsDiscoverCell*)cell {
[super configureCell:cell];
[cell setDiscoverFeedView:self.discoverFeed];
self.lastConfiguredCell = cell;
}
- (CGFloat)cellHeightForWidth:(CGFloat)width {
return self.lastConfiguredCell ? [self.lastConfiguredCell feedHeight] : 100.0;
}
@end
#pragma mark - ContentSuggestionsDiscoverCell
@interface ContentSuggestionsDiscoverCell ()
// The Discover feed which acts as the cell's content.
@property(strong, nonatomic) UIViewController* discoverFeed;
@end
@implementation ContentSuggestionsDiscoverCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.contentView.backgroundColor = [UIColor redColor];
}
return self;
}
- (void)setDiscoverFeedView:(UIViewController*)discoverFeed {
_discoverFeed = discoverFeed;
if (discoverFeed) {
UIView* discoverView = discoverFeed.view;
discoverView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:discoverView];
[NSLayoutConstraint activateConstraints:@[
[discoverView.trailingAnchor
constraintEqualToAnchor:self.contentView.trailingAnchor],
[discoverView.leadingAnchor
constraintEqualToAnchor:self.contentView.leadingAnchor],
[discoverView.topAnchor
constraintEqualToAnchor:self.contentView.topAnchor],
[discoverView.bottomAnchor
constraintEqualToAnchor:self.contentView.bottomAnchor],
]];
}
}
- (CGFloat)feedHeight {
UICollectionView* feedView;
if (!self.discoverFeed) {
return 0;
}
for (UIView* view in self.discoverFeed.view.subviews) {
if ([view isKindOfClass:[UICollectionView class]]) {
feedView = (UICollectionView*)view;
}
}
return feedView.contentSize.height;
}
@end
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#import "ios/chrome/browser/url_loading/url_loading_browser_agent.h" #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
#import "ios/chrome/browser/voice/voice_search_availability.h" #import "ios/chrome/browser/voice/voice_search_availability.h"
#import "ios/public/provider/chrome/browser/chrome_browser_provider.h" #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -154,13 +155,21 @@ ...@@ -154,13 +155,21 @@
ReadingListModel* readingListModel = ReadingListModel* readingListModel =
ReadingListModelFactory::GetForBrowserState( ReadingListModelFactory::GetForBrowserState(
self.browser->GetBrowserState()); self.browser->GetBrowserState());
UIViewController* discoverFeed =
ios::GetChromeBrowserProvider()
->GetDiscoverFeedProvider()
->NewFeedViewController(static_cast<id<ApplicationCommands>>(
self.browser->GetCommandDispatcher()));
self.contentSuggestionsMediator = [[ContentSuggestionsMediator alloc] self.contentSuggestionsMediator = [[ContentSuggestionsMediator alloc]
initWithContentService:contentSuggestionsService initWithContentService:contentSuggestionsService
largeIconService:largeIconService largeIconService:largeIconService
largeIconCache:cache largeIconCache:cache
mostVisitedSite:std::move(mostVisitedFactory) mostVisitedSite:std::move(mostVisitedFactory)
readingListModel:readingListModel readingListModel:readingListModel
prefService:prefs]; prefService:prefs
discoverFeed:discoverFeed];
self.contentSuggestionsMediator.commandHandler = self.NTPMediator; self.contentSuggestionsMediator.commandHandler = self.NTPMediator;
self.contentSuggestionsMediator.headerProvider = self.headerController; self.contentSuggestionsMediator.headerProvider = self.headerController;
self.contentSuggestionsMediator.contentArticlesExpanded = self.contentSuggestionsMediator.contentArticlesExpanded =
......
...@@ -44,37 +44,39 @@ class ReadingListModel; ...@@ -44,37 +44,39 @@ class ReadingListModel;
ContentSuggestionsMetricsRecorderDelegate> ContentSuggestionsMetricsRecorderDelegate>
// Initialize the mediator with the |contentService| to mediate. // Initialize the mediator with the |contentService| to mediate.
- (nullable instancetype) - (instancetype)
initWithContentService: initWithContentService:
(nonnull ntp_snippets::ContentSuggestionsService*)contentService (ntp_snippets::ContentSuggestionsService*)contentService
largeIconService:(nonnull favicon::LargeIconService*)largeIconService largeIconService:(favicon::LargeIconService*)largeIconService
largeIconCache:(nullable LargeIconCache*)largeIconCache largeIconCache:(LargeIconCache*)largeIconCache
mostVisitedSite: mostVisitedSite:
(std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites (std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites
readingListModel:(nonnull ReadingListModel*)readingListModel readingListModel:(ReadingListModel*)readingListModel
prefService:(nonnull PrefService*)prefService prefService:(PrefService*)prefService
discoverFeed:(UIViewController*)discoverFeed
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
// Command handler for the mediator. // Command handler for the mediator.
@property(nonatomic, weak, nullable) @property(nonatomic, weak)
id<ContentSuggestionsCommands, ContentSuggestionsGestureCommands> id<ContentSuggestionsCommands, ContentSuggestionsGestureCommands>
commandHandler; commandHandler;
@property(nonatomic, weak, nullable) id<ContentSuggestionsHeaderProvider> @property(nonatomic, weak) id<ContentSuggestionsHeaderProvider> headerProvider;
headerProvider;
// Whether the contents section should be expanded or collapsed. Collapsed // Whether the contents section should be expanded or collapsed. Collapsed
// means to show the header, but not any content or footer. // means to show the header, but not any content or footer.
@property(nullable, nonatomic, strong) @property(nonatomic, strong) PrefBackedBoolean* contentArticlesExpanded;
PrefBackedBoolean* contentArticlesExpanded;
// Whether to force the reload the Reading List section next time it is updated. // Whether to force the reload the Reading List section next time it is updated.
// Reset to NO after actual reload. // Reset to NO after actual reload.
@property(nonatomic, assign) BOOL readingListNeedsReload; @property(nonatomic, assign) BOOL readingListNeedsReload;
// ViewController created by the Discover provider containing the Discover feed.
@property(nonatomic, strong) UIViewController* discoverFeed;
// The notification promo owned by this mediator. // The notification promo owned by this mediator.
- (nonnull NotificationPromoWhatsNew*)notificationPromo; - (NotificationPromoWhatsNew*)notificationPromo;
// Block |URL| from Most Visited sites. // Block |URL| from Most Visited sites.
- (void)blockMostVisitedURL:(GURL)URL; - (void)blockMostVisitedURL:(GURL)URL;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "ios/chrome/browser/application_context.h" #include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h" #include "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h"
#include "ios/chrome/browser/pref_names.h" #include "ios/chrome/browser/pref_names.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_discover_item.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h"
...@@ -115,6 +116,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4; ...@@ -115,6 +116,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4;
// reading list count. // reading list count.
@property(nonatomic, strong) @property(nonatomic, strong)
ContentSuggestionsMostVisitedActionItem* readingListItem; ContentSuggestionsMostVisitedActionItem* readingListItem;
// Item for the Discover feed.
@property(nonatomic, strong) ContentSuggestionsDiscoverItem* discoverItem;
// Number of unread items in reading list model. // Number of unread items in reading list model.
@property(nonatomic, assign) NSInteger readingListUnreadCount; @property(nonatomic, assign) NSInteger readingListUnreadCount;
...@@ -134,7 +137,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4; ...@@ -134,7 +137,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4;
mostVisitedSite: mostVisitedSite:
(std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites (std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites
readingListModel:(ReadingListModel*)readingListModel readingListModel:(ReadingListModel*)readingListModel
prefService:(PrefService*)prefService { prefService:(PrefService*)prefService
discoverFeed:(UIViewController*)discoverFeed {
self = [super init]; self = [super init];
if (self) { if (self) {
_contentArticlesEnabled = _contentArticlesEnabled =
...@@ -157,6 +161,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4; ...@@ -157,6 +161,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4;
_learnMoreItem = [[ContentSuggestionsLearnMoreItem alloc] init]; _learnMoreItem = [[ContentSuggestionsLearnMoreItem alloc] init];
_discoverSectionInfo = DiscoverSectionInformation(); _discoverSectionInfo = DiscoverSectionInformation();
_discoverItem = [[ContentSuggestionsDiscoverItem alloc] init];
_discoverItem.discoverFeed = discoverFeed;
_notificationPromo = std::make_unique<NotificationPromoWhatsNew>( _notificationPromo = std::make_unique<NotificationPromoWhatsNew>(
GetApplicationContext()->GetLocalState()); GetApplicationContext()->GetLocalState());
...@@ -227,7 +233,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4; ...@@ -227,7 +233,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4;
[sectionsInfo addObject:self.learnMoreSectionInfo]; [sectionsInfo addObject:self.learnMoreSectionInfo];
if (IsDiscoverFeedEnabled()) { if (IsDiscoverFeedEnabled() &&
self.contentArticlesEnabled->GetValue()->GetBool()) {
[sectionsInfo addObject:self.discoverSectionInfo]; [sectionsInfo addObject:self.discoverSectionInfo];
} }
...@@ -256,6 +263,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4; ...@@ -256,6 +263,8 @@ const NSInteger kMaxNumMostVisitedTiles = 4;
[convertedSuggestions addObjectsFromArray:self.actionButtonItems]; [convertedSuggestions addObjectsFromArray:self.actionButtonItems];
} else if (sectionInfo == self.learnMoreSectionInfo) { } else if (sectionInfo == self.learnMoreSectionInfo) {
[convertedSuggestions addObject:self.learnMoreItem]; [convertedSuggestions addObject:self.learnMoreItem];
} else if (sectionInfo == self.discoverSectionInfo) {
[convertedSuggestions addObject:self.discoverItem];
} else { } else {
ntp_snippets::Category category = ntp_snippets::Category category =
[[self categoryWrapperForSectionInfo:sectionInfo] category]; [[self categoryWrapperForSectionInfo:sectionInfo] category];
...@@ -620,26 +629,43 @@ const NSInteger kMaxNumMostVisitedTiles = 4; ...@@ -620,26 +629,43 @@ const NSInteger kMaxNumMostVisitedTiles = 4;
// ntp_snippets doesn't differentiate between disabled vs collapsed, so if // ntp_snippets doesn't differentiate between disabled vs collapsed, so if
// the status is |CATEGORY_EXPLICITLY_DISABLED|, check the value of // the status is |CATEGORY_EXPLICITLY_DISABLED|, check the value of
// |contentArticlesEnabled|. // |contentArticlesEnabled|.
// The Articles category is always ignored if the Discover feed flag is
// active.
- (BOOL)isCategoryInitOrAvailable:(ntp_snippets::Category)category { - (BOOL)isCategoryInitOrAvailable:(ntp_snippets::Category)category {
ntp_snippets::CategoryStatus status = ntp_snippets::CategoryStatus status =
self.contentService->GetCategoryStatus(category); self.contentService->GetCategoryStatus(category);
if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES) && if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES)) {
status == ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) if (status == ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) {
return self.contentArticlesEnabled->GetValue()->GetBool(); return self.contentArticlesEnabled->GetValue()->GetBool() &&
else !IsDiscoverFeedEnabled();
} else {
return IsCategoryStatusInitOrAvailable(
self.contentService->GetCategoryStatus(category)) &&
!IsDiscoverFeedEnabled();
}
} else {
return IsCategoryStatusInitOrAvailable( return IsCategoryStatusInitOrAvailable(
self.contentService->GetCategoryStatus(category)); self.contentService->GetCategoryStatus(category));
}
} }
// ntp_snippets doesn't differentiate between disabled vs collapsed, so if // ntp_snippets doesn't differentiate between disabled vs collapsed, so if
// the status is |CATEGORY_EXPLICITLY_DISABLED|, check the value of // the status is |CATEGORY_EXPLICITLY_DISABLED|, check the value of
// |contentArticlesEnabled|. // |contentArticlesEnabled|.
// The Articles category is always ignored if the Discover feed flag is
// active.
- (BOOL)isCategoryAvailable:(ntp_snippets::Category)category { - (BOOL)isCategoryAvailable:(ntp_snippets::Category)category {
ntp_snippets::CategoryStatus status = ntp_snippets::CategoryStatus status =
self.contentService->GetCategoryStatus(category); self.contentService->GetCategoryStatus(category);
if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES) && if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES)) {
status == ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) { if (status == ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) {
return self.contentArticlesEnabled->GetValue()->GetBool(); return self.contentArticlesEnabled->GetValue()->GetBool() &&
!IsDiscoverFeedEnabled();
} else {
return IsCategoryStatusAvailable(
self.contentService->GetCategoryStatus(category)) &&
!IsDiscoverFeedEnabled();
}
} else { } else {
return IsCategoryStatusAvailable( return IsCategoryStatusAvailable(
self.contentService->GetCategoryStatus(category)); self.contentService->GetCategoryStatus(category));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment