chromium/ios/chrome/browser/ui/settings/password/password_sharing/family_picker_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/ui/settings/password/password_sharing/family_picker_view_controller.h"

#import "base/check.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/authentication/authentication_constants.h"
#import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h"
#import "ios/chrome/browser/ui/settings/password/password_sharing/family_picker_view_controller_presentation_delegate.h"
#import "ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_constants.h"
#import "ios/chrome/browser/ui/settings/password/password_sharing/recipient_info.h"
#import "ios/chrome/common/string_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util_mac.h"

namespace {

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

typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeRecipient = kItemTypeEnumZero,
  ItemTypeHeader,
};

// Size of the accessory view symbol.
const CGFloat kAccessorySymbolSize = 22;

}  // namespace

@interface FamilyPickerViewController () <PopoverLabelViewControllerDelegate>
@end

@implementation FamilyPickerViewController

// Contains information about the potential recipients of the user.
NSArray<RecipientInfoForIOSDisplay*>* _recipients;

#pragma mark - UIViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  UINavigationItem* navigationItem = self.navigationItem;
  navigationItem.title = l10n_util::GetNSString(IDS_IOS_PASSWORD_SHARING_TITLE);
  navigationItem.rightBarButtonItem = [self createShareButton];

  UITableView* tableView = self.tableView;
  tableView.allowsMultipleSelection = YES;
  tableView.accessibilityIdentifier = kFamilyPickerTableViewID;

  [self loadModel];
}

- (void)loadModel {
  [super loadModel];

  TableViewModel* model = self.tableViewModel;
  [model addSectionWithIdentifier:SectionIdentifierRecipients];
  [model setHeader:[self headerItem]
      forSectionWithIdentifier:SectionIdentifierRecipients];
  for (RecipientInfoForIOSDisplay* recipient in _recipients) {
    [model addItem:[self recipientItem:recipient]
        toSectionWithIdentifier:SectionIdentifierRecipients];
  }
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView*)tableView
    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
  if (_recipients[indexPath.row].isEligible) {
    [tableView cellForRowAtIndexPath:indexPath].accessoryView =
        [self checkmarkCircleIcon];
    [self setShareButtonStatus];
  }
}

- (void)tableView:(UITableView*)tableView
    didDeselectRowAtIndexPath:(NSIndexPath*)indexPath {
  if (_recipients[indexPath.row].isEligible) {
    [tableView cellForRowAtIndexPath:indexPath].accessoryView =
        [self circleIcon];
    [self setShareButtonStatus];
  }
}

- (BOOL)tableView:(UITableView*)tableView
    shouldHighlightRowAtIndexPath:(NSIndexPath*)indexPath {
  return _recipients[indexPath.row].isEligible;
}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView*)tableView
    numberOfRowsInSection:(NSInteger)section {
  return _recipients.count;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView*)theTableView {
  return 1;
}

- (UITableViewCell*)tableView:(UITableView*)tableView
        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
  UITableViewCell* cell = [super tableView:tableView
                     cellForRowAtIndexPath:indexPath];
  cell.selectionStyle = UITableViewCellSelectionStyleNone;
  cell.userInteractionEnabled = YES;
  cell.textLabel.numberOfLines = 1;
  cell.detailTextLabel.numberOfLines = 1;
  cell.accessibilityIdentifier = _recipients[indexPath.row].email;
  if (_recipients[indexPath.row].isEligible) {
    cell.accessoryView =
        [tableView.indexPathsForSelectedRows containsObject:indexPath]
            ? [self checkmarkCircleIcon]
            : [self circleIcon];
  } else {
    UIButton* infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
    [infoButton setImage:[self infoCircleIcon] forState:UIControlStateNormal];
    [infoButton addTarget:self
                   action:@selector(infoButtonTapped:)
         forControlEvents:UIControlEventTouchUpInside];
    infoButton.accessibilityIdentifier =
        [NSString stringWithFormat:@"%@ %@", kFamilyPickerInfoButtonID,
                                   _recipients[indexPath.row].email];
    infoButton.accessibilityLabel = [NSString
        stringWithFormat:@"%@, %@", _recipients[indexPath.row].fullName,
                         l10n_util::GetNSString(
                             IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT)];
    cell.accessoryView = infoButton;

    // Make the cell a non-accessible element to make the info button accessible
    // to VoiceOver.
    cell.isAccessibilityElement = NO;
    infoButton.isAccessibilityElement = YES;
  }

  return cell;
}

#pragma mark - FamilyPickerConsumer

- (void)setRecipients:(NSArray<RecipientInfoForIOSDisplay*>*)recipients {
  NSSortDescriptor* eligibility =
      [[NSSortDescriptor alloc] initWithKey:@"isEligible" ascending:NO];
  NSSortDescriptor* fullName = [[NSSortDescriptor alloc] initWithKey:@"fullName"
                                                           ascending:YES];
  _recipients =
      [recipients sortedArrayUsingDescriptors:@[ eligibility, fullName ]];

  [self loadModel];
  [self.tableView reloadData];
  if (_recipients.count == 1 && _recipients[0].isEligible) {
    [self selectFirstRow];
  }
}

#pragma mark - Items

- (TableViewLinkHeaderFooterItem*)headerItem {
  TableViewLinkHeaderFooterItem* header =
      [[TableViewLinkHeaderFooterItem alloc] initWithType:ItemTypeHeader];
  header.text =
      l10n_util::GetNSString(IDS_IOS_PASSWORD_SHARING_FAMILY_PICKER_SUBTITLE);
  return header;
}

- (SettingsImageDetailTextItem*)recipientItem:
    (RecipientInfoForIOSDisplay*)recipient {
  SettingsImageDetailTextItem* item =
      [[SettingsImageDetailTextItem alloc] initWithType:ItemTypeRecipient];
  item.text = recipient.fullName;
  item.detailText = recipient.email;
  item.image = CircularImageFromImage(recipient.profileImage,
                                      kAccountProfilePhotoDimension);
  item.accessibilityTraits = UIAccessibilityTraitButton;
  if (!recipient.isEligible) {
    item.accessibilityTraits |= UIAccessibilityTraitNotEnabled;
    item.textColor = [UIColor colorNamed:kTextTertiaryColor];
    item.detailTextColor = [UIColor colorNamed:kTextTertiaryColor];
    item.imageViewAlpha = 0.4f;
  }
  return item;
}

#pragma mark - Public

- (void)setupLeftBackButton {
  UINavigationItem* navigationItem = self.navigationItem;
  navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
      initWithTitle:l10n_util::GetNSString(
                        IDS_IOS_PASSWORD_SHARING_FAMILY_PICKER_BACK_BUTTON)
              style:UIBarButtonItemStylePlain
             target:self
             action:@selector(backButtonTapped)];
  navigationItem.leftBarButtonItem.accessibilityIdentifier =
      kFamilyPickerBackButtonID;
}

- (void)setupLeftCancelButton {
  UINavigationItem* navigationItem = self.navigationItem;
  navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
                           target:self
                           action:@selector(cancelButtonTapped)];
  navigationItem.leftBarButtonItem.accessibilityIdentifier =
      kFamilyPickerCancelButtonID;
}

#pragma mark - PopoverLabelViewControllerDelegate

- (void)didTapLinkURL:(NSURL*)URL {
  [self.delegate learnMoreLinkWasTapped];
}

#pragma mark - Private

// Creates accessory view for a cell with selected sharing recipient.
- (UIImageView*)checkmarkCircleIcon {
  return [[UIImageView alloc]
      initWithImage:DefaultSymbolWithPointSize(kCheckmarkCircleFillSymbol,
                                               kAccessorySymbolSize)];
}

// Creates accessory view for a cell with unselected sharing recipient.
- (UIImageView*)circleIcon {
  UIImageView* circleIcon = [[UIImageView alloc]
      initWithImage:DefaultSymbolWithPointSize(kCircleSymbol,
                                               kAccessorySymbolSize)];
  circleIcon.tintColor = [UIColor colorNamed:kGrey300Color];
  return circleIcon;
}

// Creates accessory view for a cell with ineligible sharing recipient.
- (UIImage*)infoCircleIcon {
  return DefaultSymbolWithPointSize(kInfoCircleSymbol, kAccessorySymbolSize);
}

// Displays popup with information about password sharing ineligibility.
- (void)infoButtonTapped:(UIButton*)button {
  NSString* text =
      l10n_util::GetNSString(IDS_IOS_PASSWORD_SHARING_FAMILY_MEMBER_INELIGIBLE);

  NSDictionary* textAttributes = @{
    NSForegroundColorAttributeName : [UIColor colorNamed:kTextSecondaryColor],
    NSFontAttributeName :
        [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
  };

  NSDictionary* linkAttributes = @{
    NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor],
    NSFontAttributeName :
        [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline],
    // Opening HC article is handled by the delegate.
    NSLinkAttributeName : @"",
  };

  PopoverLabelViewController* popoverViewController =
      [[PopoverLabelViewController alloc]
          initWithPrimaryAttributedString:AttributedStringFromStringWithLink(
                                              text, textAttributes,
                                              linkAttributes)
                secondaryAttributedString:nil];
  popoverViewController.delegate = self;
  popoverViewController.popoverPresentationController.sourceView = button;
  popoverViewController.popoverPresentationController.sourceRect =
      button.bounds;
  popoverViewController.popoverPresentationController.permittedArrowDirections =
      UIPopoverArrowDirectionAny;

  [self presentViewController:popoverViewController
                     animated:YES
                   completion:nil];
}

// Notifies the delegate that the previous view should be displayed.
- (void)backButtonTapped {
  [self.delegate familyPickerNavigatedBack:self];
}

// Notifies the delegate that the view should be dismissed.
- (void)cancelButtonTapped {
  [self.delegate familyPickerWasDismissed:self];
}

// Notifies the delegate that the user has confirmed the recipient selection.
- (void)shareButtonTapped {
  UITableView* tableView = self.tableView;
  CHECK(tableView.indexPathsForSelectedRows.count > 0u);

  NSMutableArray<RecipientInfoForIOSDisplay*>* selectedRecipients =
      [NSMutableArray array];
  for (NSIndexPath* indexPath in tableView.indexPathsForSelectedRows) {
    [selectedRecipients addObject:_recipients[indexPath.row]];
  }
  [self.delegate familyPickerClosed:self
             withSelectedRecipients:selectedRecipients];
}

// Enables share button if any row is selected or disables it otherwise.
- (void)setShareButtonStatus {
  self.navigationItem.rightBarButtonItem.enabled =
      self.tableView.indexPathsForSelectedRows.count > 0;
}

// Selects first row of the table view.
- (void)selectFirstRow {
  NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
  UITableView* tableView = self.tableView;
  [tableView selectRowAtIndexPath:indexPath
                         animated:NO
                   scrollPosition:UITableViewScrollPositionNone];
  [self tableView:tableView didSelectRowAtIndexPath:indexPath];
}

// Creates right share button.
- (UIBarButtonItem*)createShareButton {
  UIBarButtonItem* shareButton = [[UIBarButtonItem alloc]
      initWithTitle:l10n_util::GetNSString(
                        IDS_IOS_PASSWORD_SHARING_SHARE_BUTTON)
              style:UIBarButtonItemStylePlain
             target:self
             action:@selector(shareButtonTapped)];
  shareButton.enabled = NO;
  shareButton.accessibilityIdentifier = kFamilyPickerShareButtonID;
  return shareButton;
}

@end