chromium/ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_view_controller.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_view_controller.h"

#import "base/apple/foundation_util.h"
#import "base/check_op.h"
#import "base/i18n/message_formatter.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/net/model/crurl.h"
#import "ios/chrome/browser/shared/ui/list_model/list_model.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_favicon_data_source.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/synced_sessions/model/distant_tab.h"
#import "ios/chrome/browser/bring_android_tabs/ui_bundled/constants.h"
#import "ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_table_view_item.h"
#import "ios/chrome/browser/bring_android_tabs/ui_bundled/tab_list_from_android_view_controller_delegate.h"
#import "ios/chrome/common/ui/favicon/favicon_attributes.h"
#import "ios/chrome/common/ui/favicon/favicon_view.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"

namespace {

typedef NS_ENUM(NSInteger, SectionIdentifier) {
  TabListSectionIdentifier = kSectionIdentifierEnumZero,
};

}  // namespace

@implementation TabListFromAndroidViewController

- (instancetype)init {
  return [super initWithStyle:ChromeTableViewStyle()];
}

- (void)viewDidLoad {
  [super viewDidLoad];
  self.definesPresentationContext = YES;
  self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
  self.title =
      l10n_util::GetNSString(IDS_IOS_BRING_ANDROID_TABS_REVIEW_TABLE_TITLE);
  [self.tableView setDelegate:self];
  self.tableView.allowsMultipleSelectionDuringEditing = YES;
  self.tableView.allowsMultipleSelection = YES;
  [self.tableView
      setSeparatorInset:UIEdgeInsetsMake(0, kTableViewSeparatorInset, 0, 0)];
  self.tableView.estimatedRowHeight = kTabListFromAndroidCellHeight;
  self.navigationItem.leftBarButtonItem = [self navigationCancelButton];
  self.navigationItem.rightBarButtonItem = [self navigationOpenTabsButton];
  self.tableView.accessibilityIdentifier = kBringAndroidTabsPromptTabListAXId;
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  CHECK_EQ(tableView, self.tableView);
  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
  item.accessoryType = UITableViewCellAccessoryCheckmark;
  [self reconfigureCellsForItems:@[ item ]];
  [self updateOpenTabsButton];
}

- (void)tableView:(UITableView*)tableView
    didDeselectRowAtIndexPath:(NSIndexPath*)indexPath {
  CHECK_EQ(tableView, self.tableView);
  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
  item.accessoryType = UITableViewCellAccessoryNone;
  [self reconfigureCellsForItems:@[ item ]];
  [self updateOpenTabsButton];
}

- (UITableViewCell*)tableView:(UITableView*)tableView
        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
  CHECK_EQ(tableView, self.tableView);
  UITableViewCell* cell = [super tableView:tableView
                     cellForRowAtIndexPath:indexPath];
  cell.selectionStyle = UITableViewCellSelectionStyleNone;
  [self loadFaviconForCell:cell indexPath:indexPath];
  return cell;
}

#pragma mark - UIAdaptivePresentationControllerDelegate

- (void)presentationControllerDidDismiss:
    (UIPresentationController*)presentationController {
  int numberDeselected =
      [self.tableView
          numberOfRowsInSection:
              [self.tableViewModel
                  sectionForSectionIdentifier:kSectionIdentifierEnumZero]] -
      [self.tableView indexPathsForSelectedRows].count;
  [_delegate
      tabListFromAndroidViewControllerDidDismissWithSwipe:YES
                                   numberOfDeselectedTabs:numberDeselected];
}

#pragma mark - TabListFromAndroidConsumer

- (void)setTabListItems:(NSArray<TabListFromAndroidTableViewItem*>*)items {
  [self loadModel];

  [self.tableViewModel addSectionWithIdentifier:TabListSectionIdentifier];
  for (TabListFromAndroidTableViewItem* item : items) {
    item.accessoryType = UITableViewCellAccessoryCheckmark;
    [self.tableViewModel addItem:item
         toSectionWithIdentifier:TabListSectionIdentifier];
  }
  // Tabs are selected by default.
  NSInteger section = [self.tableViewModel
      sectionForSectionIdentifier:TabListSectionIdentifier];
  for (NSInteger index = 0;
       index < [self.tableViewModel numberOfItemsInSection:section]; index++) {
    [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index
                                                            inSection:section]
                                animated:NO
                          scrollPosition:UITableViewScrollPositionNone];
  }

  [self updateOpenTabsButton];
}

#pragma mark - Helpers

// Retrieves favicon from FaviconLoader and sets FaviconView in given `cell`.
- (void)loadFaviconForCell:(UITableViewCell*)cell
                 indexPath:(NSIndexPath*)indexPath {
  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
  CHECK(item);
  CHECK(cell);
  TabListFromAndroidTableViewItem* tabListItem =
      base::apple::ObjCCastStrict<TabListFromAndroidTableViewItem>(item);
  TabListFromAndroidTableViewCell* tabListCell =
      base::apple::ObjCCastStrict<TabListFromAndroidTableViewCell>(cell);

  NSString* itemIdentifier = tabListItem.uniqueIdentifier;
  [_faviconDataSource
      faviconForPageURL:tabListItem.URL
             completion:^(FaviconAttributes* attributes) {
               // Only set favicon if the cell hasn't been reused.
               if ([tabListCell.cellUniqueIdentifier
                       isEqualToString:itemIdentifier]) {
                 [tabListCell.faviconView configureWithAttributes:attributes];
               }
             }];
}

// Returns the navigation 'cancel' button.
- (UIBarButtonItem*)navigationCancelButton {
  UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
                           target:self
                           action:@selector(cancelButtonTapped)];
  cancelButton.accessibilityIdentifier =
      kBringAndroidTabsPromptTabListCancelButtonAXId;
  return cancelButton;
}

// Updates the title and enabled status of the 'Open Tabs' button. This method
// is called and the button is recreated every time the user selects/de-selects
// a table row.
- (void)updateOpenTabsButton {
  UIBarButtonItem* rightBarButton = self.navigationItem.rightBarButtonItem;
  rightBarButton.title = [self openTabsButtonTitle];
  int numberSelectedTabs = [self.tableView indexPathsForSelectedRows].count;
  if (numberSelectedTabs == 0) {
    [rightBarButton setEnabled:NO];
  } else {
    [rightBarButton setEnabled:YES];
  }
}

// Returns the title of the navigation 'Open Tabs' button.
- (NSString*)openTabsButtonTitle {
  int numberSelectedTabs = [self.tableView indexPathsForSelectedRows].count;
  return base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
      IDS_IOS_BRING_ANDROID_TABS_REVIEW_TABLE_OPEN_BUTTON, numberSelectedTabs));
}

// Returns the navigation 'Open Tabs' button.
- (UIBarButtonItem*)navigationOpenTabsButton {
  UIBarButtonItem* openButton =
      [[UIBarButtonItem alloc] initWithTitle:[self openTabsButtonTitle]
                                       style:UIBarButtonItemStylePlain
                                      target:self
                                      action:@selector(openButtonTapped)];
  openButton.accessibilityIdentifier =
      kBringAndroidTabsPromptTabListOpenButtonAXId;
  return openButton;
}

// Called when the cancel button is tapped.
- (void)cancelButtonTapped {
  int numberDeselected =
      [self.tableView
          numberOfRowsInSection:
              [self.tableViewModel
                  sectionForSectionIdentifier:kSectionIdentifierEnumZero]] -
      [self.tableView indexPathsForSelectedRows].count;
  [_delegate
      tabListFromAndroidViewControllerDidDismissWithSwipe:NO
                                   numberOfDeselectedTabs:numberDeselected];
  [self.presentingViewController dismissViewControllerAnimated:YES
                                                    completion:nil];
}

//  Called when the open tabs button is tapped.
- (void)openButtonTapped {
  [_delegate tabListFromAndroidViewControllerDidTapOpenButtonWithTabIndices:
                 [self selectedTabIndices]];
  [self.presentingViewController dismissViewControllerAnimated:YES
                                                    completion:nil];
}

// Returns an array with the indices of the tabs the user has selected. Indices
// correspond to the indices of the list of user's tabs retrieved from
// BringAndroidTabsToIOSService.
- (NSArray<NSNumber*>*)selectedTabIndices {
  NSMutableArray<NSNumber*>* tabIndices = [[NSMutableArray alloc] init];
  for (NSIndexPath* indexPath in self.tableView.indexPathsForSelectedRows) {
    [tabIndices addObject:@(indexPath.row)];
  }
  return tabIndices;
}

@end