// Copyright 2019 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/send_tab_to_self/send_tab_to_self_table_view_controller.h"
#import <utility>
#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "base/feature_list.h"
#import "base/strings/string_number_conversions.h"
#import "base/strings/sys_string_conversions.h"
#import "components/send_tab_to_self/features.h"
#import "components/send_tab_to_self/send_tab_to_self_model.h"
#import "components/send_tab_to_self/target_device_info.h"
#import "components/sync_device_info/device_info.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_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_button_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_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/send_tab_to_self/send_tab_to_self_image_detail_text_item.h"
#import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_manage_devices_item.h"
#import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_modal_delegate.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/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"
namespace {
// Accessibility identifier of the Modal Cancel Button.
NSString* const kSendTabToSelfModalCancelButton =
@"kSendTabToSelfModalCancelButton";
// Accessibility identifier of the Modal Cancel Button.
NSString* const kSendTabToSelfModalSendButton =
@"kSendTabToSelfModalSendButton";
CGFloat kSymbolSize = 22;
} // namespace
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeSend = kItemTypeEnumZero,
ItemTypeDevice,
ItemTypeNoTargetDevice,
ItemTypeManageDevices,
};
@interface SendTabToSelfTableViewController () {
// The list of devices with thier names, cache_guids, device types,
// and active times.
std::vector<send_tab_to_self::TargetDeviceInfo> _targetDeviceList;
}
// Item that holds the currently selected device.
@property(nonatomic, strong) SendTabToSelfImageDetailTextItem* selectedItem;
// Delegate to handle dismisal and event actions.
@property(nonatomic, weak) id<SendTabToSelfModalDelegate> delegate;
// Avatar of the account sharing a tab.
@property(nonatomic, strong) UIImage* accountAvatar;
// Email of the account sharing a tab.
@property(nonatomic, strong) NSString* accountEmail;
// Item that holds the cancel Button for this modal dialog.
@property(nonatomic, strong) TableViewTextButtonItem* sendToDevice;
@end
@implementation SendTabToSelfTableViewController
- (instancetype)initWithDeviceList:
(std::vector<send_tab_to_self::TargetDeviceInfo>)
targetDeviceList
delegate:(id<SendTabToSelfModalDelegate>)delegate
accountAvatar:(UIImage*)accountAvatar
accountEmail:(NSString*)accountEmail {
self = [super initWithStyle:UITableViewStylePlain];
if (self) {
_targetDeviceList = std::move(targetDeviceList);
_delegate = delegate;
_accountEmail = accountEmail;
_accountAvatar = accountAvatar;
}
return self;
}
#pragma mark - ViewController Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
self.styler.cellBackgroundColor =
[UIColor colorNamed:kPrimaryBackgroundColor];
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 0;
// Configure the NavigationBar.
UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:@selector(dismiss:)];
cancelButton.accessibilityIdentifier = kSendTabToSelfModalCancelButton;
self.navigationItem.leftBarButtonItem = cancelButton;
self.navigationItem.title =
l10n_util::GetNSString(IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_TITLE);
self.navigationController.navigationBar.prefersLargeTitles = NO;
[self loadModel];
}
#pragma mark - TableViewModel
- (void)loadModel {
[super loadModel];
TableViewModel* model = self.tableViewModel;
[model addSectionWithIdentifier:kSectionIdentifierEnumZero];
if (_targetDeviceList.empty()) {
TableViewTextItem* noTargetDeviceItem =
[[TableViewTextItem alloc] initWithType:ItemTypeNoTargetDevice];
noTargetDeviceItem.text =
l10n_util::GetNSString(IDS_SEND_TAB_TO_SELF_NO_TARGET_DEVICE_LABEL);
noTargetDeviceItem.textAlignment = NSTextAlignmentLeft;
noTargetDeviceItem.textFont =
[UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
[model addItem:noTargetDeviceItem
toSectionWithIdentifier:kSectionIdentifierEnumZero];
} else {
[self addDeviceItems];
}
SendTabToSelfManageDevicesItem* manageDevicesItem =
[[SendTabToSelfManageDevicesItem alloc]
initWithType:ItemTypeManageDevices];
manageDevicesItem.accountAvatar = self.accountAvatar;
manageDevicesItem.accountEmail = self.accountEmail;
manageDevicesItem.showManageDevicesLink = !_targetDeviceList.empty();
manageDevicesItem.delegate = self.delegate;
[model addItem:manageDevicesItem
toSectionWithIdentifier:kSectionIdentifierEnumZero];
if (_targetDeviceList.empty()) {
// No need for the send button if there are no devices.
return;
}
self.sendToDevice =
[[TableViewTextButtonItem alloc] initWithType:ItemTypeSend];
self.sendToDevice.buttonText =
l10n_util::GetNSString(IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ACTION);
self.sendToDevice.buttonTextColor =
[UIColor colorNamed:kSolidButtonTextColor];
self.sendToDevice.buttonBackgroundColor = [UIColor colorNamed:kBlueColor];
self.sendToDevice.boldButtonText = NO;
self.sendToDevice.accessibilityIdentifier = kSendTabToSelfModalSendButton;
[model addItem:self.sendToDevice
toSectionWithIdentifier:kSectionIdentifierEnumZero];
}
#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]);
if (itemType == ItemTypeSend) {
TableViewTextButtonCell* tableViewTextButtonCell =
base::apple::ObjCCastStrict<TableViewTextButtonCell>(cell);
[tableViewTextButtonCell.button addTarget:self
action:@selector(sendTabWhenPressed:)
forControlEvents:UIControlEventTouchUpInside];
}
// Hide the separator for the last row ("manage devices" item) by maxing out
// the left margin. For other cells, use a standard value.
CGFloat separatorLeftMargin = itemType == ItemTypeManageDevices
? self.tableView.bounds.size.width
: kTableViewHorizontalSpacing;
cell.separatorInset = UIEdgeInsetsMake(0.f, separatorLeftMargin, 0.f, 0.f);
return cell;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView*)tableView
didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
DCHECK(item);
if (item.type == ItemTypeDevice) {
SendTabToSelfImageDetailTextItem* imageDetailTextItem =
base::apple::ObjCCastStrict<SendTabToSelfImageDetailTextItem>(item);
if (imageDetailTextItem == self.selectedItem) {
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
return;
}
self.selectedItem.selected = NO;
imageDetailTextItem.selected = YES;
[self reconfigureCellsForItems:@[ self.selectedItem, imageDetailTextItem ]];
self.selectedItem = imageDetailTextItem;
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
#pragma mark - Helpers
- (void)sendTabWhenPressed:(UIButton*)sender {
[self.delegate sendTabToTargetDeviceCacheGUID:self.selectedItem.cacheGuid
targetDeviceName:self.selectedItem.text];
}
- (void)dismiss:(UIButton*)sender {
[self.delegate dismissViewControllerAnimated];
}
- (NSString*)sendTabToSelfdaysSinceLastUpdate:(int)days {
NSString* active_time;
if (days == 0) {
active_time = l10n_util::GetNSString(
IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ITEM_SUBTITLE_TODAY);
} else if (days == 1) {
active_time = l10n_util::GetNSString(
IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ITEM_SUBTITLE_DAY);
} else {
active_time = l10n_util::GetNSStringF(
IDS_IOS_SEND_TAB_TO_SELF_TARGET_DEVICE_ITEM_SUBTITLE_DAYS,
base::NumberToString16(days));
}
return active_time;
}
- (void)addDeviceItems {
for (auto iter = _targetDeviceList.begin(); iter != _targetDeviceList.end();
++iter) {
int daysSinceLastUpdate =
(base::Time::Now() - iter->last_updated_timestamp).InDays();
SendTabToSelfImageDetailTextItem* deviceItem =
[[SendTabToSelfImageDetailTextItem alloc] initWithType:ItemTypeDevice];
deviceItem.text = base::SysUTF8ToNSString(iter->device_name);
deviceItem.detailText =
[self sendTabToSelfdaysSinceLastUpdate:daysSinceLastUpdate];
switch (iter->form_factor) {
case syncer::DeviceInfo::FormFactor::kTablet:
deviceItem.iconImage = MakeSymbolMonochrome(
DefaultSymbolWithPointSize(kIPadSymbol, kSymbolSize));
break;
case syncer::DeviceInfo::FormFactor::kPhone:
deviceItem.iconImage = MakeSymbolMonochrome(
DefaultSymbolWithPointSize(kIPhoneSymbol, kSymbolSize));
break;
case syncer::DeviceInfo::FormFactor::kDesktop:
deviceItem.iconImage = MakeSymbolMonochrome(
DefaultSymbolWithPointSize(kLaptopSymbol, kSymbolSize));
break;
default:
deviceItem.iconImage = MakeSymbolMonochrome(
DefaultSymbolWithPointSize(kLaptopSymbol, kSymbolSize));
break;
}
if (iter == _targetDeviceList.begin()) {
deviceItem.selected = YES;
self.selectedItem = deviceItem;
}
deviceItem.cacheGuid = base::SysUTF8ToNSString(iter->cache_guid);
[self.tableViewModel addItem:deviceItem
toSectionWithIdentifier:kSectionIdentifierEnumZero];
}
}
@end