chromium/ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.mm

// Copyright 2018 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/shared/ui/table_view/cells/table_view_link_header_footer_item.h"

#import "base/check_op.h"
#import "base/containers/contains.h"
#import "ios/chrome/browser/net/model/crurl.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/string_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/common/ui/util/text_view_util.h"
#import "net/base/apple/url_conversions.h"

namespace {

// Padding used on the top and bottom edges of the cell.
const CGFloat kVerticalPadding = 8;

// Horizontal padding used to align the header/footer with the section items.
const CGFloat kHorizontalSpacingToAlignWithItems = 16.0;

}  // namespace

@implementation TableViewLinkHeaderFooterItem {
  NSArray<CrURL*>* urls_;
}

- (instancetype)initWithType:(NSInteger)type {
  self = [super initWithType:type];
  if (self) {
    self.cellClass = [TableViewLinkHeaderFooterView class];
  }
  return self;
}

#pragma mark Properties

- (NSArray<CrURL*>*)urls {
  return urls_;
}

- (void)setUrls:(NSArray<CrURL*>*)urls {
  for (CrURL* url in urls_) {
    DCHECK(url.gurl.is_valid());
  }
  urls_ = urls;
}

#pragma mark CollectionViewItem

- (void)configureHeaderFooterView:(TableViewLinkHeaderFooterView*)headerFooter
                       withStyler:(ChromeTableViewStyler*)styler {
  [super configureHeaderFooterView:headerFooter withStyler:styler];

  if ([self.urls count] != 0) {
    headerFooter.urls = self.urls;
  }

  if (self.forceIndents) {
    [headerFooter setForceIndents:YES];
  }

  UIColor* textColor = self.textColor
                           ? self.textColor
                           : [UIColor colorNamed:kTextSecondaryColor];
  [headerFooter setText:self.text withColor:textColor];
}

@end

@interface TableViewLinkHeaderFooterView () <UITextViewDelegate>

// UITextView corresponding to `text` from the item.
@property(nonatomic, readonly, strong) UITextView* textView;

@end

@implementation TableViewLinkHeaderFooterView {
  NSArray<CrURL*>* urls_;
  // Leading constaint for item.
  NSLayoutConstraint* leadingConstraint_;
  // Trailing constraint for item.
  NSLayoutConstraint* trailingConstraint_;
}

@synthesize textView = _textView;

- (instancetype)initWithReuseIdentifier:(NSString*)reuseIdentifier {
  self = [super initWithReuseIdentifier:reuseIdentifier];
  if (self) {
    urls_ = @[];
    _textView = CreateUITextViewWithTextKit1();
    _textView.scrollEnabled = NO;
    _textView.editable = NO;
    _textView.delegate = self;
    _textView.backgroundColor = UIColor.clearColor;
    _textView.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
    _textView.adjustsFontForContentSizeCategory = YES;
    _textView.translatesAutoresizingMaskIntoConstraints = NO;
    _textView.linkTextAttributes =
        @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]};
    _textView.textContainer.lineFragmentPadding = 0;
    _textView.textContainerInset = UIEdgeInsetsZero;

    [self.contentView addSubview:_textView];

    leadingConstraint_ = [_textView.leadingAnchor
        constraintEqualToAnchor:self.contentView.leadingAnchor
                       constant:HorizontalPadding()];
    trailingConstraint_ = [_textView.trailingAnchor
        constraintEqualToAnchor:self.contentView.trailingAnchor
                       constant:-HorizontalPadding()];

    [NSLayoutConstraint activateConstraints:@[
      [_textView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor
                                          constant:kVerticalPadding],
      [_textView.bottomAnchor
          constraintEqualToAnchor:self.contentView.bottomAnchor
                         constant:-kVerticalPadding],
      trailingConstraint_,
      leadingConstraint_,
    ]];
  }
  return self;
}

- (void)prepareForReuse {
  [super prepareForReuse];
  self.textView.text = nil;
  self.textView.selectable = YES;
  self.textView.linkTextAttributes =
      @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]};
  self.delegate = nil;
  self.urls = @[];
  self.forceIndents = NO;
}

#pragma mark - Properties

- (void)setText:(NSString*)text withColor:(UIColor*)color {
  [self setText:text withColor:color textAlignment:NSTextAlignmentNatural];
}

- (void)setText:(NSString*)text
        withColor:(UIColor*)color
    textAlignment:(NSTextAlignment)textAlignment {
  StringWithTags parsedString = ParseStringWithLinks(text);
  NSMutableParagraphStyle* paragraphStyle =
      [[NSMutableParagraphStyle alloc] init];
  paragraphStyle.alignment = textAlignment;

  NSDictionary* textAttributes = @{
    NSFontAttributeName :
        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote],
    NSForegroundColorAttributeName : color,
    NSParagraphStyleAttributeName : paragraphStyle
  };

  NSMutableAttributedString* attributedText =
      [[NSMutableAttributedString alloc] initWithString:parsedString.string
                                             attributes:textAttributes];

  DCHECK_EQ(parsedString.ranges.size(), [self.urls count]);
  size_t index = 0;
  for (CrURL* url in self.urls) {
    [attributedText addAttribute:NSLinkAttributeName
                           value:url.nsurl
                           range:parsedString.ranges[index]];
    index += 1;
  }

  self.textView.attributedText = attributedText;
}

- (NSArray<CrURL*>*)urls {
  return urls_;
}

- (void)setUrls:(NSArray<CrURL*>*)urls {
  for (CrURL* url in urls_) {
    DCHECK(url.gurl.is_valid());
  }
  urls_ = urls;
}

- (void)setForceIndents:(BOOL)forceIndents {
  leadingConstraint_.constant =
      forceIndents ? kHorizontalSpacingToAlignWithItems : HorizontalPadding();
  trailingConstraint_.constant =
      forceIndents ? -kHorizontalSpacingToAlignWithItems : -HorizontalPadding();
}

- (void)setLinkEnabled:(BOOL)enabled {
  self.textView.selectable = enabled;
  _textView.linkTextAttributes = @{
    NSForegroundColorAttributeName :
        [UIColor colorNamed:enabled ? kBlueColor : kDisabledTintColor]
  };
}

#pragma mark - UITextViewDelegate

- (BOOL)textView:(UITextView*)textView
    shouldInteractWithURL:(NSURL*)URL
                  inRange:(NSRange)characterRange
              interaction:(UITextItemInteraction)interaction {
  DCHECK(self.textView == textView);
  CrURL* crurl = [[CrURL alloc] initWithNSURL:URL];
  DCHECK(crurl.gurl.is_valid());
  // DCHECK(base::Contains(self.urls, gURL));
  [self.delegate view:self didTapLinkURL:crurl];
  // Returns NO as the app is handling the opening of the URL.
  return NO;
}

- (void)textViewDidChangeSelection:(UITextView*)textView {
  // Always force the `selectedTextRange` to `nil` to prevent users from
  // selecting text. Setting the `selectable` property to `NO` doesn't help
  // since it makes links inside the text view untappable.
  textView.selectedTextRange = nil;
}

@end