Commit 74bafc2d authored by Kurt Horimoto's avatar Kurt Horimoto Committed by Commit Bot

[iOS] Create ReadingListCoordinator.

This CL creates ReadingListCoordinator, which uses UITableViews and
replaces the old UICollectionView-based implementation.

Bug: 805209
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I59acbe85cb389c69462bb72ddb7858acb1249c55
Reviewed-on: https://chromium-review.googlesource.com/1104941
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Reviewed-by: default avatarSergio Collazos <sczs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#572157}
parent 7c182420
...@@ -180,6 +180,7 @@ ...@@ -180,6 +180,7 @@
#import "ios/chrome/browser/ui/qr_scanner/requirements/qr_scanner_presenting.h" #import "ios/chrome/browser/ui/qr_scanner/requirements/qr_scanner_presenting.h"
#import "ios/chrome/browser/ui/reading_list/legacy_reading_list_coordinator.h" #import "ios/chrome/browser/ui/reading_list/legacy_reading_list_coordinator.h"
#import "ios/chrome/browser/ui/reading_list/offline_page_native_content.h" #import "ios/chrome/browser/ui/reading_list/offline_page_native_content.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h" #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
#include "ios/chrome/browser/ui/rtl_geometry.h" #include "ios/chrome/browser/ui/rtl_geometry.h"
...@@ -487,7 +488,7 @@ NSString* const kBrowserViewControllerSnackbarCategory = ...@@ -487,7 +488,7 @@ NSString* const kBrowserViewControllerSnackbarCategory =
scoped_refptr<VoiceSearchController> _voiceSearchController; scoped_refptr<VoiceSearchController> _voiceSearchController;
// Used to display the Reading List. // Used to display the Reading List.
LegacyReadingListCoordinator* _readingListCoordinator; ChromeCoordinator* _readingListCoordinator;
// Used to display the Find In Page UI. Nil if not visible. // Used to display the Find In Page UI. Nil if not visible.
FindBarControllerIOS* _findBarController; FindBarControllerIOS* _findBarController;
...@@ -4694,10 +4695,16 @@ applicationCommandEndpoint:(id<ApplicationCommands>)applicationCommandEndpoint { ...@@ -4694,10 +4695,16 @@ applicationCommandEndpoint:(id<ApplicationCommands>)applicationCommandEndpoint {
} }
- (void)showReadingList { - (void)showReadingList {
_readingListCoordinator = [[LegacyReadingListCoordinator alloc] _readingListCoordinator =
initWithBaseViewController:self experimental_flags::IsReadingListUIRebootEnabled()
browserState:self.browserState ? [[ReadingListCoordinator alloc]
loader:self]; initWithBaseViewController:self
browserState:self.browserState
loader:self]
: [[LegacyReadingListCoordinator alloc]
initWithBaseViewController:self
browserState:self.browserState
loader:self];
[_readingListCoordinator start]; [_readingListCoordinator start];
} }
......
...@@ -10,6 +10,8 @@ source_set("reading_list") { ...@@ -10,6 +10,8 @@ source_set("reading_list") {
"offline_page_native_content.mm", "offline_page_native_content.mm",
"reading_list_collection_view_item.h", "reading_list_collection_view_item.h",
"reading_list_collection_view_item.mm", "reading_list_collection_view_item.mm",
"reading_list_coordinator.h",
"reading_list_coordinator.mm",
"reading_list_list_item.h", "reading_list_list_item.h",
"reading_list_list_item_custom_action_factory.h", "reading_list_list_item_custom_action_factory.h",
"reading_list_list_item_custom_action_factory.mm", "reading_list_list_item_custom_action_factory.mm",
...@@ -53,6 +55,8 @@ source_set("reading_list") { ...@@ -53,6 +55,8 @@ source_set("reading_list") {
"//ios/chrome/browser/ui/reading_list/resources:distillation_fail_new", "//ios/chrome/browser/ui/reading_list/resources:distillation_fail_new",
"//ios/chrome/browser/ui/side_swipe", "//ios/chrome/browser/ui/side_swipe",
"//ios/chrome/browser/ui/static_content", "//ios/chrome/browser/ui/static_content",
"//ios/chrome/browser/ui/table_view",
"//ios/chrome/browser/ui/table_view:presentation",
"//ios/chrome/browser/ui/table_view:styler", "//ios/chrome/browser/ui/table_view:styler",
"//ios/chrome/browser/ui/table_view/cells", "//ios/chrome/browser/ui/table_view/cells",
"//ios/chrome/browser/ui/util", "//ios/chrome/browser/ui/util",
......
// Copyright 2018 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_READING_LIST_READING_LIST_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COORDINATOR_H_
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
@protocol UrlLoader;
// Coordinator for Reading List, displaying the Reading List when starting.
@interface ReadingListCoordinator : ChromeCoordinator
// Designated initialier for a ReadingListCoordinator that loads reading list
// items using |loader|.
- (nullable instancetype)
initWithBaseViewController:(nullable UIViewController*)viewController
browserState:(nonnull ios::ChromeBrowserState*)browserState
loader:(nullable id<UrlLoader>)loader
NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithBaseViewController:
(nullable UIViewController*)viewController NS_UNAVAILABLE;
- (nullable instancetype)
initWithBaseViewController:(nullable UIViewController*)viewController
browserState:(nullable ios::ChromeBrowserState*)browserState
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COORDINATOR_H_
// Copyright 2018 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/reading_list/reading_list_coordinator.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/sys_string_conversions.h"
#include "components/reading_list/core/reading_list_entry.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
#import "ios/chrome/browser/metrics/new_tab_page_uma.h"
#include "ios/chrome/browser/reading_list/offline_url_utils.h"
#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
#import "ios/chrome/browser/ui/reading_list/context_menu/reading_list_context_menu_commands.h"
#import "ios/chrome/browser/ui/reading_list/context_menu/reading_list_context_menu_coordinator.h"
#import "ios/chrome/browser/ui/reading_list/context_menu/reading_list_context_menu_params.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_list_item_factory.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_list_view_controller_audience.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_list_view_controller_delegate.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_mediator.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.h"
#import "ios/chrome/browser/ui/table_view/table_view_animator.h"
#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
#import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
#import "ios/chrome/browser/ui/url_loader.h"
#import "ios/chrome/browser/ui/util/pasteboard_util.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/web/public/referrer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface ReadingListCoordinator ()<ReadingListContextMenuCommands,
ReadingListListViewControllerAudience,
ReadingListListViewControllerDelegate,
UIViewControllerTransitioningDelegate>
// Whether the coordinator is started.
@property(nonatomic, assign, getter=isStarted) BOOL started;
// The URL loader used to load pages that have been added to the reading list.
@property(nonatomic, strong) id<UrlLoader> loader;
// The mediator that updates the table for model changes.
@property(nonatomic, strong) ReadingListMediator* mediator;
// The navigation controller displaying self.tableViewController.
@property(nonatomic, strong)
TableViewNavigationController* navigationController;
// The view controller used to display the reading list.
@property(nonatomic, strong)
ReadingListTableViewController* tableViewController;
// The coordinator used to show the context menu.
@property(nonatomic, strong)
ReadingListContextMenuCoordinator* contextMenuCoordinator;
@end
@implementation ReadingListCoordinator
@synthesize started = _started;
@synthesize loader = _loader;
@synthesize mediator = _mediator;
@synthesize navigationController = _navigationController;
@synthesize tableViewController = _tableViewController;
@synthesize contextMenuCoordinator = _contextMenuCoordinator;
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
browserState:
(ios::ChromeBrowserState*)browserState
loader:(id<UrlLoader>)loader {
self = [super initWithBaseViewController:viewController
browserState:browserState];
if (self) {
_loader = loader;
}
return self;
}
#pragma mark - Accessors
- (void)setContextMenuCoordinator:
(ReadingListContextMenuCoordinator*)contextMenuCoordinator {
if (_contextMenuCoordinator == contextMenuCoordinator)
return;
[_contextMenuCoordinator stop];
_contextMenuCoordinator = contextMenuCoordinator;
}
#pragma mark - ChromeCoordinator
- (void)start {
if (self.started)
return;
// Create the mediator.
ReadingListModel* model =
ReadingListModelFactory::GetInstance()->GetForBrowserState(
self.browserState);
favicon::LargeIconService* largeIconService =
IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState);
ReadingListListItemFactory* itemFactory =
[ReadingListListItemFactory tableViewItemFactory];
self.mediator = [[ReadingListMediator alloc] initWithModel:model
largeIconService:largeIconService
listItemFactory:itemFactory];
// Create the table.
self.tableViewController = [[ReadingListTableViewController alloc] init];
self.tableViewController.delegate = self;
self.tableViewController.audience = self;
self.tableViewController.dataSource = self.mediator;
itemFactory.accessibilityDelegate = self.tableViewController;
// Add the "Done" button and hook it up to |stop|.
UIBarButtonItem* dismissButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(stop)];
[dismissButton
setAccessibilityIdentifier:kTableViewNavigationDismissButtonId];
self.tableViewController.navigationItem.rightBarButtonItem = dismissButton;
// Present RecentTabsNavigationController.
self.navigationController = [[TableViewNavigationController alloc]
initWithTable:self.tableViewController];
self.navigationController.toolbarHidden = NO;
self.navigationController.transitioningDelegate = self;
self.navigationController.modalPresentationStyle = UIModalPresentationCustom;
[self.baseViewController presentViewController:self.navigationController
animated:YES
completion:nil];
[super start];
self.started = YES;
}
- (void)stop {
if (!self.started)
return;
self.contextMenuCoordinator = nil;
[self.tableViewController willBeDismissed];
[self.navigationController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
self.tableViewController = nil;
self.navigationController = nil;
[super stop];
self.started = NO;
}
#pragma mark - ReadingListListViewControllerAudience
- (void)readingListHasItems:(BOOL)hasItems {
self.navigationController.toolbarHidden = !hasItems;
}
#pragma mark - ReadingListContextMenuCommands
- (void)openURLInNewTabForContextMenuWithParams:
(ReadingListContextMenuParams*)params {
[self loadEntryURL:params.entryURL
withOfflineURL:GURL::EmptyGURL()
inNewTab:YES
incognito:NO];
}
- (void)openURLInNewIncognitoTabForContextMenuWithParams:
(ReadingListContextMenuParams*)params {
[self loadEntryURL:params.entryURL
withOfflineURL:GURL::EmptyGURL()
inNewTab:YES
incognito:YES];
}
- (void)copyURLForContextMenuWithParams:(ReadingListContextMenuParams*)params {
StoreURLInPasteboard(params.entryURL);
self.contextMenuCoordinator = nil;
}
- (void)openOfflineURLInNewTabForContextMenuWithParams:
(ReadingListContextMenuParams*)params {
[self loadEntryURL:params.entryURL
withOfflineURL:params.offlineURL
inNewTab:YES
incognito:NO];
}
- (void)cancelReadingListContextMenuWithParams:
(ReadingListContextMenuParams*)params {
self.contextMenuCoordinator = nil;
}
#pragma mark - ReadingListTableViewControllerDelegate
- (void)dismissReadingListListViewController:(UIViewController*)viewController {
DCHECK_EQ(self.tableViewController, viewController);
[self.tableViewController willBeDismissed];
[self stop];
}
- (void)readingListListViewController:(UIViewController*)viewController
displayContextMenuForItem:(id<ReadingListListItem>)item
atPoint:(CGPoint)menuLocation {
DCHECK_EQ(self.tableViewController, viewController);
const ReadingListEntry* entry = [self.mediator entryFromItem:item];
if (!entry) {
[self.tableViewController reloadData];
return;
}
const GURL entryURL = entry->URL();
GURL offlineURL;
if (entry->DistilledState() == ReadingListEntry::PROCESSED) {
offlineURL = reading_list::OfflineURLForPath(
entry->DistilledPath(), entryURL, entry->DistilledURL());
}
ReadingListContextMenuParams* params =
[[ReadingListContextMenuParams alloc] init];
params.title = base::SysUTF8ToNSString(entry->Title());
params.message = base::SysUTF8ToNSString(entryURL.spec());
params.rect = CGRectMake(menuLocation.x, menuLocation.y, 0, 0);
params.view = self.tableViewController.tableView;
params.entryURL = entryURL;
params.offlineURL = offlineURL;
self.contextMenuCoordinator = [[ReadingListContextMenuCoordinator alloc]
initWithBaseViewController:self.navigationController
params:params];
self.contextMenuCoordinator.commandHandler = self;
[self.contextMenuCoordinator start];
}
- (void)readingListListViewController:(UIViewController*)viewController
openItem:(id<ReadingListListItem>)item {
DCHECK_EQ(self.tableViewController, viewController);
const ReadingListEntry* entry = [self.mediator entryFromItem:item];
if (!entry) {
[self.tableViewController reloadData];
return;
}
[self loadEntryURL:entry->URL()
withOfflineURL:GURL::EmptyGURL()
inNewTab:NO
incognito:NO];
}
- (void)readingListListViewController:(UIViewController*)viewController
openItemInNewTab:(id<ReadingListListItem>)item
incognito:(BOOL)incognito {
DCHECK_EQ(self.tableViewController, viewController);
const ReadingListEntry* entry = [self.mediator entryFromItem:item];
if (!entry) {
[self.tableViewController reloadData];
return;
}
[self loadEntryURL:entry->URL()
withOfflineURL:GURL::EmptyGURL()
inNewTab:YES
incognito:incognito];
}
- (void)readingListListViewController:(UIViewController*)viewController
openItemOfflineInNewTab:(id<ReadingListListItem>)item {
DCHECK_EQ(self.tableViewController, viewController);
const ReadingListEntry* entry = [self.mediator entryFromItem:item];
if (!entry)
return;
if (entry->DistilledState() == ReadingListEntry::PROCESSED) {
const GURL entryURL = entry->URL();
GURL offlineURL = reading_list::OfflineURLForPath(
entry->DistilledPath(), entryURL, entry->DistilledURL());
[self loadEntryURL:entry->URL()
withOfflineURL:offlineURL
inNewTab:YES
incognito:NO];
}
}
#pragma mark - UIViewControllerTransitioningDelegate
- (UIPresentationController*)
presentationControllerForPresentedViewController:(UIViewController*)presented
presentingViewController:(UIViewController*)presenting
sourceViewController:(UIViewController*)source {
return [[TableViewPresentationController alloc]
initWithPresentedViewController:presented
presentingViewController:presenting];
}
- (id<UIViewControllerAnimatedTransitioning>)
animationControllerForPresentedController:(UIViewController*)presented
presentingController:(UIViewController*)presenting
sourceController:(UIViewController*)source {
UITraitCollection* traitCollection = presenting.traitCollection;
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
// Use the default animator for fullscreen presentations.
return nil;
}
TableViewAnimator* animator = [[TableViewAnimator alloc] init];
animator.presenting = YES;
return animator;
}
- (id<UIViewControllerAnimatedTransitioning>)
animationControllerForDismissedController:(UIViewController*)dismissed {
UITraitCollection* traitCollection = dismissed.traitCollection;
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
// Use the default animator for fullscreen presentations.
return nil;
}
TableViewAnimator* animator = [[TableViewAnimator alloc] init];
animator.presenting = NO;
return animator;
}
#pragma mark - URL Loading Helpers
// Loads reading list URLs. If |offlineURL| is valid, the item will be loaded
// offline; otherwise |entryURL| is loaded. |newTab| and |incognito| can be
// used to optionally open the URL in a new tab or in incognito. The
// coordinator is also stopped after the load is requested.
- (void)loadEntryURL:(const GURL&)entryURL
withOfflineURL:(const GURL&)offlineURL
inNewTab:(BOOL)newTab
incognito:(BOOL)incognito {
DCHECK(entryURL.is_valid());
base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
new_tab_page_uma::RecordAction(
self.browserState, new_tab_page_uma::ACTION_OPENED_READING_LIST_ENTRY);
// Load the offline URL if available.
GURL loadURL = entryURL;
if (offlineURL.is_valid()) {
loadURL = offlineURL;
// Offline URLs should always be opened in new tabs.
newTab = YES;
// Record the offline load and update the model.
UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", true);
const GURL updateURL = entryURL;
[self.mediator markEntryRead:updateURL];
}
// Prepare the table for dismissal.
[self.tableViewController willBeDismissed];
// Use a referrer with a specific URL to signal that this entry should not be
// taken into account for the Most Visited tiles.
web::Referrer referrer =
web::Referrer(GURL(kReadingListReferrerURL), web::ReferrerPolicyDefault);
if (newTab) {
[self.loader webPageOrderedOpen:loadURL
referrer:referrer
inIncognito:incognito
inBackground:NO
appendTo:kLastTab];
} else {
web::NavigationManager::WebLoadParams params(loadURL);
params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
params.referrer = web::Referrer(GURL(kReadingListReferrerURL),
web::ReferrerPolicyDefault);
[self.loader loadURLWithParams:params];
}
[self stop];
}
@end
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