chromium/ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_browsing_data_view_controller.mm

// Copyright 2024 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/settings/clear_browsing_data/quick_delete_browsing_data_view_controller.h"

#import "base/strings/sys_string_conversions.h"
#import "components/strings/grit/components_strings.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/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/ui/settings/cells/clear_browsing_data_constants.h"
#import "ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_browsing_data_view_controller_delegate.h"
#import "ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_mutator.h"
#import "ios/chrome/browser/ui/settings/clear_browsing_data/quick_delete_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util_mac.h"

namespace {

// Browing data type icon size.
const CGFloat kDefaultSymbolSize = 24;

// TableView's footer section height.
constexpr CGFloat kSectionFooterHeight = 0;

// The URL for signing out of Chrome from Delete Browsing Data (DBD).
const char kDBDSignOutOfChromeURL[] = "settings://DBDSignOutOfChrome";

// Section identifiers in the browsing data page table view.
typedef NS_ENUM(NSInteger, SectionIdentifier) {
  SectionIdentifierBrowsingData = kSectionIdentifierEnumZero,
  SectionIdentifierFooter,
};

// Item identifiers in the browsing data page table view.
typedef NS_ENUM(NSInteger, ItemIdentifier) {
  ItemIdentifierHistory = kItemTypeEnumZero,
  ItemIdentifierTabs,
  ItemIdentifierSiteData,
  ItemIdentifierCache,
  ItemIdentifierPasswords,
  ItemIdentifierAutofill,
};

}  // namespace

@interface QuickDeleteBrowsingDataViewController () <
    TableViewLinkHeaderFooterItemDelegate> {
  UITableViewDiffableDataSource<NSNumber*, NSNumber*>* _dataSource;
  browsing_data::TimePeriod _timeRange;
  NSString* _historySummary;
  NSString* _tabsSummary;
  NSString* _cacheSummary;
  NSString* _passwordsSummary;
  NSString* _autofillSummary;
  BOOL _historySelected;
  BOOL _tabsSelected;
  BOOL _siteDataSelected;
  BOOL _cacheSelected;
  BOOL _passwordsSelected;
  BOOL _autofillSelected;
  BOOL _shouldShowFooter;
}
@end

@implementation QuickDeleteBrowsingDataViewController

#pragma mark - ChromeTableViewController

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

#pragma mark - UIViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.navigationItem.largeTitleDisplayMode =
      UINavigationItemLargeTitleDisplayModeNever;
  self.navigationItem.leftBarButtonItem = [self cancelButton];
  self.navigationItem.rightBarButtonItem = [self confirmButton];
  self.title = l10n_util::GetNSString(IDS_IOS_DELETE_BROWSING_DATA_TITLE);
  [self loadModel];
}

- (void)loadModel {
  __weak __typeof(self) weakSelf = self;
  _dataSource = [[UITableViewDiffableDataSource alloc]
      initWithTableView:self.tableView
           cellProvider:^UITableViewCell*(UITableView* tableView,
                                          NSIndexPath* indexPath,
                                          NSNumber* itemIdentifier) {
             return
                 [weakSelf cellForTableView:tableView
                                  indexPath:indexPath
                             itemIdentifier:static_cast<ItemIdentifier>(
                                                itemIdentifier.integerValue)];
           }];

  RegisterTableViewCell<TableViewDetailIconCell>(self.tableView);
  RegisterTableViewHeaderFooter<TableViewLinkHeaderFooterView>(self.tableView);

  NSDiffableDataSourceSnapshot* snapshot =
      [[NSDiffableDataSourceSnapshot alloc] init];
  [snapshot appendSectionsWithIdentifiers:@[
    @(SectionIdentifierBrowsingData), @(SectionIdentifierFooter)
  ]];
  [snapshot appendItemsWithIdentifiers:@[
    @(ItemIdentifierHistory), @(ItemIdentifierTabs), @(ItemIdentifierSiteData),
    @(ItemIdentifierCache), @(ItemIdentifierPasswords),
    @(ItemIdentifierAutofill)
  ]
             intoSectionWithIdentifier:@(SectionIdentifierBrowsingData)];

  [_dataSource applySnapshot:snapshot animatingDifferences:NO];
}

#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  [tableView deselectRowAtIndexPath:indexPath animated:YES];
  ItemIdentifier itemIdentifier = static_cast<ItemIdentifier>(
      [_dataSource itemIdentifierForIndexPath:indexPath].integerValue);

  // Update selection value for the corresponding cell with `itemIdentifier`.
  [self updateSelectionForItemIdentifier:itemIdentifier];

  // Update the snapshot for the selected cell.
  [self updateSnapshotForItemIdentifier:itemIdentifier];
}

- (UIView*)tableView:(UITableView*)tableView
    viewForFooterInSection:(NSInteger)section {
  SectionIdentifier sectionIdentifier = static_cast<SectionIdentifier>(
      [_dataSource sectionIdentifierForIndex:section].integerValue);
  switch (sectionIdentifier) {
    case SectionIdentifierFooter: {
      if (!_shouldShowFooter) {
        return nil;
      }
      TableViewLinkHeaderFooterView* footer =
          DequeueTableViewHeaderFooter<TableViewLinkHeaderFooterView>(
              tableView);
      footer.accessibilityIdentifier = kQuickDeleteBrowsingDataFooterIdentifier;
      footer.delegate = self;
      footer.urls =
          @[ [[CrURL alloc] initWithGURL:GURL(kDBDSignOutOfChromeURL)] ];
      [footer setText:l10n_util::GetNSString(
                          IDS_IOS_DELETE_BROWSING_DATA_PAGE_FOOTER)
            withColor:[UIColor colorNamed:kTextSecondaryColor]];
      return footer;
    }
    case SectionIdentifierBrowsingData: {
      return nil;
    }
  }
  NOTREACHED();
}

- (CGFloat)tableView:(UITableView*)tableView
    heightForFooterInSection:(NSInteger)section {
  SectionIdentifier sectionIdentifier = static_cast<SectionIdentifier>(
      [_dataSource sectionIdentifierForIndex:section].integerValue);
  if (sectionIdentifier == SectionIdentifierFooter && _shouldShowFooter) {
    return UITableViewAutomaticDimension;
  }
  return kSectionFooterHeight;
}

#pragma mark - TableViewLinkHeaderFooterItemDelegate

- (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(CrURL*)url {
  CHECK(url.gurl == kDBDSignOutOfChromeURL);
  [_delegate signOutAndShowActionSheet];
}

#pragma mark - QuickDeleteConsumer

- (void)setTimeRange:(browsing_data::TimePeriod)timeRange {
  if (_timeRange == timeRange) {
    return;
  }
  _timeRange = timeRange;
}

- (void)setBrowsingDataSummary:(NSString*)summary {
  // TODO(crbug.com/353211728): Remove this after refactoring the main page
  // summary to use the new methods for results & selection.
}

- (void)setShouldShowFooter:(BOOL)shouldShowFooter {
  if (_shouldShowFooter == shouldShowFooter) {
    return;
  }

  _shouldShowFooter = shouldShowFooter;

  // Reload the footer section.
  NSDiffableDataSourceSnapshot<NSNumber*, NSNumber*>* snapshot =
      [_dataSource snapshot];
  [snapshot reloadSectionsWithIdentifiers:@[ @(SectionIdentifierFooter) ]];
  [_dataSource applySnapshot:snapshot animatingDifferences:YES];
}

- (void)updateHistoryWithResult:
    (const browsing_data::BrowsingDataCounter::Result&)result {
  _historySummary =
      quick_delete_util::GetCounterTextFromResult(result, _timeRange);
  [self updateSnapshotForItemIdentifier:ItemIdentifierHistory];
}

- (void)updateTabsWithResult:
    (const browsing_data::BrowsingDataCounter::Result&)result {
  _tabsSummary =
      quick_delete_util::GetCounterTextFromResult(result, _timeRange);
  [self updateSnapshotForItemIdentifier:ItemIdentifierTabs];
}

- (void)updateCacheWithResult:
    (const browsing_data::BrowsingDataCounter::Result&)result {
  _cacheSummary =
      quick_delete_util::GetCounterTextFromResult(result, _timeRange);
  [self updateSnapshotForItemIdentifier:ItemIdentifierCache];
}

- (void)updatePasswordsWithResult:
    (const browsing_data::BrowsingDataCounter::Result&)result {
  _passwordsSummary =
      quick_delete_util::GetCounterTextFromResult(result, _timeRange);
  [self updateSnapshotForItemIdentifier:ItemIdentifierPasswords];
}

- (void)updateAutofillWithResult:
    (const browsing_data::BrowsingDataCounter::Result&)result {
  _autofillSummary =
      quick_delete_util::GetCounterTextFromResult(result, _timeRange);
  [self updateSnapshotForItemIdentifier:ItemIdentifierAutofill];
}

- (void)setHistorySelection:(BOOL)selected {
  _historySelected = selected;
  [self updateSnapshotForItemIdentifier:ItemIdentifierHistory];
}

- (void)setTabsSelection:(BOOL)selected {
  _tabsSelected = selected;
  [self updateSnapshotForItemIdentifier:ItemIdentifierTabs];
}

- (void)setSiteDataSelection:(BOOL)selected {
  _siteDataSelected = selected;
  [self updateSnapshotForItemIdentifier:ItemIdentifierSiteData];
}

- (void)setCacheSelection:(BOOL)selected {
  _cacheSelected = selected;
  [self updateSnapshotForItemIdentifier:ItemIdentifierCache];
}

- (void)setPasswordsSelection:(BOOL)selected {
  _passwordsSelected = selected;
  [self updateSnapshotForItemIdentifier:ItemIdentifierPasswords];
}

- (void)setAutofillSelection:(BOOL)selected {
  _autofillSelected = selected;
  [self updateSnapshotForItemIdentifier:ItemIdentifierAutofill];
}

- (void)deletionInProgress {
  NOTREACHED();
}

- (void)deletionFinished {
  NOTREACHED();
}

#pragma mark - Private

// Returns the cancel button on the navigation bar.
- (UIBarButtonItem*)cancelButton {
  UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
                           target:self
                           action:@selector(onCancel:)];
  return cancelButton;
}

// Returns the confirm button on the navigation bar.
- (UIBarButtonItem*)confirmButton {
  UIBarButtonItem* confirmButton = [[UIBarButtonItem alloc]
      initWithTitle:l10n_util::GetNSString(IDS_IOS_DELETE_BROWSING_DATA_CONFIRM)
              style:UIBarButtonItemStylePlain
             target:self
             action:@selector(onConfirm:)];
  confirmButton.accessibilityIdentifier =
      kQuickDeleteBrowsingDataConfirmButtonIdentifier;
  return confirmButton;
}

// Dismisses the page without saving changes in selection.
- (void)onCancel:(id)sender {
  [_delegate dismissBrowsingDataPage];
}

// Notifies the mutator of the confirmation of the browsing data types
// selection.
- (void)onConfirm:(id)sender {
  [_mutator updateHistorySelection:_historySelected];
  [_mutator updateTabsSelection:_tabsSelected];
  [_mutator updateSiteDataSelection:_siteDataSelected];
  [_mutator updateCacheSelection:_cacheSelected];
  [_mutator updatePasswordsSelection:_passwordsSelected];
  [_mutator updateAutofillSelection:_autofillSelected];
  [_delegate dismissBrowsingDataPage];
}

// Creates the browsing data cell.
- (TableViewDetailIconCell*)createCellWithTitle:(NSString*)title
                                        summary:(NSString*)summary
                                           icon:(UIImage*)icon
                                       selected:(BOOL)selected
                        accessibilityIdentifier:
                            (NSString*)accessibilityIdentifier {
  TableViewDetailIconCell* cell =
      DequeueTableViewCell<TableViewDetailIconCell>(self.tableView);
  cell.textLabel.text = title;
  // Placeholder description required by the constraint to avoid cell resize.
  cell.detailText = summary ? summary : @" ";
  cell.textLayoutConstraintAxis = UILayoutConstraintAxisVertical;
  [cell setIconImage:icon
            tintColor:[UIColor colorNamed:kGrey500Color]
      backgroundColor:cell.backgroundColor
         cornerRadius:0];
  cell.accessoryType = selected ? UITableViewCellAccessoryCheckmark
                                : UITableViewCellAccessoryNone;
  cell.accessibilityIdentifier = accessibilityIdentifier;
  cell.accessibilityTraits |= UIAccessibilityTraitButton;
  return cell;
}

// Returns the cell for the corresponding `itemIdentifier`.
- (UITableViewCell*)cellForTableView:(UITableView*)tableView
                           indexPath:(NSIndexPath*)indexPath
                      itemIdentifier:(ItemIdentifier)itemIdentifier {
  switch (itemIdentifier) {
    case ItemIdentifierHistory: {
      return [self
              createCellWithTitle:l10n_util::GetNSString(
                                      IDS_IOS_CLEAR_BROWSING_HISTORY)
                          summary:_historySummary
                             icon:[self iconForItemIdentifier:itemIdentifier]
                         selected:_historySelected
          accessibilityIdentifier:kQuickDeleteBrowsingDataHistoryIdentifier];
    }
    case ItemIdentifierTabs: {
      return [self
              createCellWithTitle:l10n_util::GetNSString(IDS_IOS_CLOSE_TABS)
                          summary:_tabsSummary
                             icon:[self iconForItemIdentifier:itemIdentifier]
                         selected:_tabsSelected
          accessibilityIdentifier:kQuickDeleteBrowsingDataTabsIdentifier];
    }
    case ItemIdentifierSiteData: {
      // Because there is no counter for site data, an explanatory text is
      // displayed.
      return [self
              createCellWithTitle:l10n_util::GetNSString(IDS_IOS_CLEAR_COOKIES)
                          summary:l10n_util::GetNSString(
                                      IDS_DEL_COOKIES_COUNTER)
                             icon:[self iconForItemIdentifier:itemIdentifier]
                         selected:_siteDataSelected
          accessibilityIdentifier:kQuickDeleteBrowsingDataSiteDataIdentifier];
    }
    case ItemIdentifierCache: {
      return [self
              createCellWithTitle:l10n_util::GetNSString(IDS_IOS_CLEAR_CACHE)
                          summary:_cacheSummary
                             icon:[self iconForItemIdentifier:itemIdentifier]
                         selected:_cacheSelected
          accessibilityIdentifier:kQuickDeleteBrowsingDataCacheIdentifier];
    }
    case ItemIdentifierPasswords: {
      return [self
              createCellWithTitle:l10n_util::GetNSString(
                                      IDS_IOS_CLEAR_SAVED_PASSWORDS)
                          summary:_passwordsSummary
                             icon:[self iconForItemIdentifier:itemIdentifier]
                         selected:_passwordsSelected
          accessibilityIdentifier:kQuickDeleteBrowsingDataPasswordsIdentifier];
    }
    case ItemIdentifierAutofill: {
      return [self
              createCellWithTitle:l10n_util::GetNSString(IDS_IOS_CLEAR_AUTOFILL)
                          summary:_autofillSummary
                             icon:[self iconForItemIdentifier:itemIdentifier]
                         selected:_autofillSelected
          accessibilityIdentifier:kQuickDeleteBrowsingDataAutofillIdentifier];
    }
  }
}

// Reloads the snapshot for the cell with the given `itemIdentifier`.
- (void)updateSnapshotForItemIdentifier:(ItemIdentifier)itemIdentifier {
  NSDiffableDataSourceSnapshot<NSNumber*, NSNumber*>* snapshot =
      [_dataSource snapshot];
  [snapshot reloadItemsWithIdentifiers:@[ @(itemIdentifier) ]];
  [_dataSource applySnapshot:snapshot animatingDifferences:YES];
}

// Toggles the selection for the given `itemIdentifier`.
- (void)updateSelectionForItemIdentifier:(ItemIdentifier)itemIdentifier {
  switch (itemIdentifier) {
    case ItemIdentifierHistory: {
      _historySelected = !_historySelected;
      break;
    }
    case ItemIdentifierTabs: {
      _tabsSelected = !_tabsSelected;
      break;
    }
    case ItemIdentifierSiteData: {
      _siteDataSelected = !_siteDataSelected;
      break;
    }
    case ItemIdentifierCache: {
      _cacheSelected = !_cacheSelected;
      break;
    }
    case ItemIdentifierPasswords: {
      _passwordsSelected = !_passwordsSelected;
      break;
    }
    case ItemIdentifierAutofill: {
      _autofillSelected = !_autofillSelected;
      break;
    }
  }
}

// Returns the icon for the given `itemIdentifier`.
- (UIImage*)iconForItemIdentifier:(ItemIdentifier)itemIdentifier {
  switch (itemIdentifier) {
    case ItemIdentifierHistory: {
      return DefaultSymbolTemplateWithPointSize(kHistorySymbol,
                                                kDefaultSymbolSize);
    }
    case ItemIdentifierTabs: {
      return DefaultSymbolTemplateWithPointSize(kTabsSymbol,
                                                kDefaultSymbolSize);
    }
    case ItemIdentifierSiteData: {
      return DefaultSymbolTemplateWithPointSize(kInfoCircleSymbol,
                                                kDefaultSymbolSize);
    }
    case ItemIdentifierCache: {
      return DefaultSymbolTemplateWithPointSize(kCachedDataSymbol,
                                                kDefaultSymbolSize);
    }
    case ItemIdentifierPasswords: {
      return CustomSymbolTemplateWithPointSize(kPasswordSymbol,
                                               kDefaultSymbolSize);
    }
    case ItemIdentifierAutofill: {
      return DefaultSymbolTemplateWithPointSize(kAutofillDataSymbol,
                                                kDefaultSymbolSize);
    }
  }
}

@end