chromium/ios/chrome/browser/ui/tab_switcher/tab_grid/suggested_actions/suggested_actions_view_controller.mm

// Copyright 2022 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/ui/tab_switcher/tab_grid/suggested_actions/suggested_actions_view_controller.h"

#import <utility>

#import "base/apple/foundation_util.h"
#import "base/check_op.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_image_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_tabs_search_suggested_history_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_url_item.h"
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_styler.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/suggested_actions/suggested_actions_delegate.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"

namespace {
const CGFloat kEstimatedRowMaxHeight = 150;
NSString* const kSuggestedActionsViewControllerAccessibilityIdentifier =
    @"search_suggestions_view_controller";
const int kSectionIdentifierSuggestedActions = kSectionIdentifierEnumZero + 1;

}  // namespace

typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeSuggestedActionSearchRecentTabs = kItemTypeEnumZero,
  ItemTypeSuggestedActionSearchWeb,
  ItemTypeSuggestedActionSearchHistory,
};

@interface SuggestedActionsViewController ()

// Delegate to handle the execution of the suggested actions.
@property(nonatomic, weak) id<SuggestedActionsDelegate>
    suggestedActionsDelegate;
// YES, if all the cells were loaded at least once.
@property(nonatomic, assign) BOOL allCellsLoaded;

@end

@implementation SuggestedActionsViewController

- (instancetype)initWithDelegate:
    (id<SuggestedActionsViewControllerDelegate>)delegate {
  self = [super initWithStyle:UITableViewStyleGrouped];

  if (self) {
    _delegate = delegate;
    self.styler.tableViewBackgroundColor =
        [UIColor colorNamed:kGridBackgroundColor];
  }
  return self;
}

#pragma mark - ViewController Lifecycle

- (void)viewDidLoad {
  [super viewDidLoad];
  self.view.accessibilityIdentifier =
      kSuggestedActionsViewControllerAccessibilityIdentifier;
  self.tableView.cellLayoutMarginsFollowReadableWidth = YES;
  self.tableView.estimatedRowHeight = kEstimatedRowMaxHeight;
  self.tableView.estimatedSectionHeaderHeight = 0.0;
  self.tableView.rowHeight = UITableViewAutomaticDimension;
  self.tableView.sectionFooterHeight = 0.0;
  self.tableView.alwaysBounceVertical = NO;
  self.tableView.scrollEnabled = NO;
  self.allCellsLoaded = NO;
  // The TableView header and footer are set to some default size when they are
  // set to nil, and that will result on empty space on the top and the bottom
  // of the table. To workaround that an empty frame is created and set on both
  // the header and the footer of the TableView.
  CGRect frame = CGRectZero;
  frame.size.height = CGFLOAT_MIN;
  [self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:frame]];
  [self.tableView setTableFooterView:[[UIView alloc] initWithFrame:frame]];

  self.tableView.layer.cornerRadius = kGridCellCornerRadius;
  [self loadModel];
  [self.tableView reloadData];
  [self.tableView layoutIfNeeded];
}

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  [self loadModel];
  [self.tableView reloadData];
}

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  self.allCellsLoaded = YES;
}

#pragma mark - TableViewModel

- (void)loadModel {
  [super loadModel];

  TableViewModel* model = self.tableViewModel;
  [model addSectionWithIdentifier:kSectionIdentifierSuggestedActions];
  UIColor* actionsTextColor = [UIColor colorNamed:kBlueColor];
  TableViewImageItem* searchWebItem = [[TableViewImageItem alloc]
      initWithType:ItemTypeSuggestedActionSearchWeb];
  searchWebItem.title =
      l10n_util::GetNSString(IDS_IOS_TABS_SEARCH_SUGGESTED_ACTION_SEARCH_WEB);
  searchWebItem.image = [[UIImage imageNamed:@"suggested_action_web"]
      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  searchWebItem.textColor = actionsTextColor;
  [model addItem:searchWebItem
      toSectionWithIdentifier:kSectionIdentifierSuggestedActions];

  if (!IsTabGroupSyncEnabled()) {
    TableViewImageItem* searchRecentTabsItem = [[TableViewImageItem alloc]
        initWithType:ItemTypeSuggestedActionSearchRecentTabs];
    searchRecentTabsItem.title = l10n_util::GetNSString(
        IDS_IOS_TABS_SEARCH_SUGGESTED_ACTION_SEARCH_RECENT_TABS);
    searchRecentTabsItem.image =
        [[UIImage imageNamed:@"suggested_action_recent_tabs"]
            imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    searchRecentTabsItem.textColor = actionsTextColor;
    [model addItem:searchRecentTabsItem
        toSectionWithIdentifier:kSectionIdentifierSuggestedActions];
  }

  TableViewTabsSearchSuggestedHistoryItem* searchHistoryItem =
      [[TableViewTabsSearchSuggestedHistoryItem alloc]
          initWithType:ItemTypeSuggestedActionSearchHistory];
  searchHistoryItem.textColor = actionsTextColor;
  [model addItem:searchHistoryItem
      toSectionWithIdentifier:kSectionIdentifierSuggestedActions];
}

#pragma mark - UITableViewDataSource

- (UITableViewCell*)tableView:(UITableView*)tableView
        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
  UITableViewCell* cell = [super tableView:tableView
                     cellForRowAtIndexPath:indexPath];
  ItemType itemType = static_cast<ItemType>(
      [self.tableViewModel itemTypeForIndexPath:indexPath]);

  // Update the history search result count once available.
  if (itemType == ItemTypeSuggestedActionSearchHistory &&
      self.searchText.length) {
    __weak TableViewTabsSearchSuggestedHistoryCell* weakCell =
        base::apple::ObjCCastStrict<TableViewTabsSearchSuggestedHistoryCell>(
            cell);
    NSString* currentSearchText = self.searchText;
    weakCell.searchTerm = currentSearchText;
    [self.delegate suggestedActionsViewController:self
           fetchHistoryResultsCountWithCompletion:^(size_t resultCount) {
             if ([weakCell.searchTerm isEqualToString:currentSearchText]) {
               [weakCell updateHistoryResultsCount:resultCount];
             }
           }];
  }
  return cell;
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  DCHECK_EQ(tableView, self.tableView);
  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
  NSInteger itemTypeSelected =
      [self.tableViewModel itemTypeForIndexPath:indexPath];
  switch (itemTypeSelected) {
    case ItemTypeSuggestedActionSearchRecentTabs:
      [self.delegate
          didSelectSearchRecentTabsInSuggestedActionsViewController:self];
      break;
    case ItemTypeSuggestedActionSearchWeb:
      [self.delegate didSelectSearchWebInSuggestedActionsViewController:self];
      break;
    case ItemTypeSuggestedActionSearchHistory:
      [self.delegate
          didSelectSearchHistoryInSuggestedActionsViewController:self];
      break;
  }
}

#pragma mark - Public

- (void)setSearchText:(NSString*)searchText {
  _searchText = searchText;
  [self.tableView reloadData];
}

- (CGFloat)contentHeight {
  if (!self.allCellsLoaded) {
    // If all the cells have not been loaded at least once, load them so this
    // method can return an accurate height.
    int rowsCount = [self.tableView numberOfRowsInSection:0];
    for (int row = 0; row < rowsCount; row++) {
      [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForItem:row
                                                                inSection:0]];
    }
    self.allCellsLoaded = YES;
  }
  return self.tableView.contentSize.height;
}

@end