chromium/ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_mediator.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/notifications/content_notifications/content_notifications_mediator.h"

#import "base/apple/foundation_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/user_metrics.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "components/prefs/pref_service.h"
#import "ios/chrome/browser/content_notification/model/content_notification_nau_configuration.h"
#import "ios/chrome/browser/content_notification/model/content_notification_service.h"
#import "ios/chrome/browser/content_notification/model/content_notification_settings_action.h"
#import "ios/chrome/browser/push_notification/model/constants.h"
#import "ios/chrome/browser/push_notification/model/push_notification_account_context_manager.h"
#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
#import "ios/chrome/browser/push_notification/model/push_notification_profile_service.h"
#import "ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h"
#import "ios/chrome/browser/push_notification/model/push_notification_service.h"
#import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
#import "ios/chrome/browser/push_notification/model/push_notification_util.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.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_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_item.h"
#import "ios/chrome/browser/ui/push_notification/notifications_alert_presenter.h"
#import "ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_constants.h"
#import "ios/chrome/browser/ui/settings/notifications/content_notifications/content_notifications_consumer.h"
#import "ios/chrome/browser/ui/settings/notifications/notifications_constants.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"

// List of items.
typedef NS_ENUM(NSInteger, ItemType) {
  ItemTypeContentNotifications = kItemTypeEnumZero,
  ItemTypeSportsNotifications,
  ItemTypeContentFooter,
};

@interface ContentNotificationsMediator ()

// Items for the Content Notifications settings.
@property(nonatomic, strong, readonly)
    TableViewSwitchItem* contentNotificationsItem;
// Items for the Sports Notifications settings.
@property(nonatomic, strong, readonly)
    TableViewSwitchItem* sportsNotificationsItem;
// Header item.
@property(nonatomic, strong)
    TableViewLinkHeaderFooterItem* contentNotificationsFooterItem;

// Pref service to retrieve preference values.
@property(nonatomic, assign) PrefService* prefService;

@end

@implementation ContentNotificationsMediator {
  // Identity object that contains the user's account details.
  std::string _gaiaID;
}

@synthesize contentNotificationsItem = _contentNotificationsItem;
@synthesize contentNotificationsFooterItem = _contentNotificationsFooterItem;
@synthesize sportsNotificationsItem = _sportsNotificationsItem;

- (instancetype)initWithPrefService:(PrefService*)prefs
                             gaiaID:(const std::string&)gaiaID {
  self = [super init];
  if (self) {
    DCHECK(prefs);
    _prefService = prefs;
    _gaiaID = gaiaID;
  }

  return self;
}

#pragma mark - Public

- (void)deniedPermissionsForClientIds:
    (std::vector<PushNotificationClientId>)clientIds {
  for (PushNotificationClientId clientID : clientIds) {
    [self switchItemForClientId:clientID].on = push_notification_settings::
        GetMobileNotificationPermissionStatusForClient(clientID, _gaiaID);
  }
  [self.consumer reloadData];
}

#pragma mark - Properties

- (TableViewSwitchItem*)contentNotificationsItem {
  if (!_contentNotificationsItem) {
    _contentNotificationsItem = [self
             switchItemWithType:ItemTypeContentNotifications
                           text:
                               l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_NOTIFICATIONS_PERSONALIZED_NEWS_SETTINGS_TOGGLE_TITLE)
                         symbol:DefaultSettingsRootSymbol(kNewspaperSFSymbol)
                     symbolTint:UIColor.whiteColor
          symbolBackgroundColor:[UIColor colorNamed:kPink500Color]
              symbolBorderWidth:0
        accessibilityIdentifier:kContentNotificationsCellId];
    _contentNotificationsItem.on = push_notification_settings::
        GetMobileNotificationPermissionStatusForClient(
            PushNotificationClientId::kContent, _gaiaID);
  }
  return _contentNotificationsItem;
}

- (TableViewSwitchItem*)sportsNotificationsItem {
  if (!_sportsNotificationsItem) {
    _sportsNotificationsItem = [self
             switchItemWithType:ItemTypeSportsNotifications
                           text:
                               l10n_util::GetNSString(
                                   IDS_IOS_CONTENT_NOTIFICATIONS_SPORTS_SETTINGS_TOGGLE_TITLE)
                         symbol:DefaultSettingsRootSymbol(kMedalSymbol)
                     symbolTint:UIColor.whiteColor
          symbolBackgroundColor:[UIColor colorNamed:kPink500Color]
              symbolBorderWidth:0
        accessibilityIdentifier:kSportsNotificationsCellId];
    _sportsNotificationsItem.on = push_notification_settings::
        GetMobileNotificationPermissionStatusForClient(
            PushNotificationClientId::kSports, _gaiaID);
  }
  return _sportsNotificationsItem;
}

- (TableViewLinkHeaderFooterItem*)contentNotificationsFooterItem {
  if (!_contentNotificationsFooterItem) {
    _contentNotificationsFooterItem = [[TableViewLinkHeaderFooterItem alloc]
        initWithType:ItemTypeContentFooter];
    _contentNotificationsFooterItem.text = l10n_util::GetNSString(
        IDS_IOS_CONTENT_NOTIFICATIONS_CONTENT_SETTINGS_FOOTER_TEXT);
  }

  return _contentNotificationsFooterItem;
}

- (void)setConsumer:(id<ContentNotificationsConsumer>)consumer {
  if (_consumer == consumer) {
    return;
  }
  _consumer = consumer;
  [_consumer setContentNotificationsItem:self.contentNotificationsItem];
  [_consumer setSportsNotificationsItem:self.sportsNotificationsItem];
  [_consumer
      setContentNotificationsFooterItem:self.contentNotificationsFooterItem];
}

#pragma mark - ContentNotificationsViewControllerDelegate

- (void)didToggleSwitchItem:(TableViewItem*)item withValue:(BOOL)value {
  ItemType type = static_cast<ItemType>(item.type);
  switch (type) {
    case ItemTypeContentNotifications: {
      if (value) {
        [self.presenter presentPushNotificationPermissionAlertWithClientIds:
                            {PushNotificationClientId::kContent}];
        [self recordSettingsActionHistogramForAction:
                  ContentNotificationSettingsToggleAction::kEnabledContent];
      } else {
        [self disablePreferenceFor:PushNotificationClientId::kContent];
        self.contentNotificationsItem.on = push_notification_settings::
            GetMobileNotificationPermissionStatusForClient(
                PushNotificationClientId::kContent, _gaiaID);
        [self recordSettingsActionHistogramForAction:
                  ContentNotificationSettingsToggleAction::kDisabledContent];
      }
      [self sendNAUForPreferenceChangeWithClientID:PushNotificationClientId::
                                                       kContent
                                             value:value];
      break;
    }
    case ItemTypeSportsNotifications: {
      if (value) {
        [self.presenter presentPushNotificationPermissionAlertWithClientIds:
                            {PushNotificationClientId::kSports}];
        [self recordSettingsActionHistogramForAction:
                  ContentNotificationSettingsToggleAction::kEnabledSports];
      } else {
        [self disablePreferenceFor:PushNotificationClientId::kSports];
        self.sportsNotificationsItem.on = push_notification_settings::
            GetMobileNotificationPermissionStatusForClient(
                PushNotificationClientId::kSports, _gaiaID);
        [self recordSettingsActionHistogramForAction:
                  ContentNotificationSettingsToggleAction::kDisabledSports];
      }
      [self sendNAUForPreferenceChangeWithClientID:PushNotificationClientId::
                                                       kSports
                                             value:value];
      break;
    }
    default:
      // Not a switch.
      NOTREACHED_IN_MIGRATION();
      break;
  }
}

#pragma mark - Private

- (TableViewSwitchItem*)switchItemWithType:(NSInteger)type
                                      text:(NSString*)text
                                    symbol:(UIImage*)symbol
                                symbolTint:(UIColor*)tint
                     symbolBackgroundColor:(UIColor*)backgroundColor
                         symbolBorderWidth:(CGFloat)borderWidth
                   accessibilityIdentifier:(NSString*)accessibilityIdentifier {
  TableViewSwitchItem* switchItem =
      [[TableViewSwitchItem alloc] initWithType:type];
  switchItem.text = text;
  switchItem.accessibilityIdentifier = accessibilityIdentifier;
  switchItem.iconImage = symbol;
  switchItem.iconTintColor = tint;
  switchItem.iconCornerRadius = kColorfulBackgroundSymbolCornerRadius;
  switchItem.iconBackgroundColor = backgroundColor;
  switchItem.iconBorderWidth = borderWidth;

  return switchItem;
}

// Updates the current user's permission preference for the given `client_id`.
- (void)disablePreferenceFor:(PushNotificationClientId)clientID {
  PushNotificationService* service =
      GetApplicationContext()->GetPushNotificationService();
  service->SetPreference(base::SysUTF8ToNSString(_gaiaID), clientID, false);
}

// Sends an NAU when any of the settings preferences have been updated.
- (void)sendNAUForPreferenceChangeWithClientID:
            (PushNotificationClientId)clientID
                                         value:(BOOL)value {
  ContentNotificationSettingsAction* settingsAction =
      [[ContentNotificationSettingsAction alloc] init];
  settingsAction.toggleStatus = value;
  switch (clientID) {
    case PushNotificationClientId::kContent:
      settingsAction.toggleChanged = SettingsToggleTypeContent;
      break;
    case PushNotificationClientId::kSports:
      settingsAction.toggleChanged = SettingsToggleTypeSports;
      break;
    case PushNotificationClientId::kCommerce:
    case PushNotificationClientId::kTips:
    case PushNotificationClientId::kSafetyCheck:
      // This should never be reached.
      DCHECK(FALSE);
      break;
  }
  [PushNotificationUtil
      getPermissionSettings:^(UNNotificationSettings* settings) {
        settingsAction.currentAuthorizationStatus =
            settings.authorizationStatus;
        ContentNotificationNAUConfiguration* config =
            [[ContentNotificationNAUConfiguration alloc] init];
        config.settingsAction = settingsAction;
        self.contentNotificationService->SendNAUForConfiguration(config);
      }];
}

// Returns the TableViewSwitchItem for the given `clientId`.
- (TableViewSwitchItem*)switchItemForClientId:
    (PushNotificationClientId)clientId {
  switch (clientId) {
    case PushNotificationClientId::kContent:
      return _contentNotificationsItem;
    case PushNotificationClientId::kSports:
      return _sportsNotificationsItem;
    case PushNotificationClientId::kTips:
    case PushNotificationClientId::kSafetyCheck:
    case PushNotificationClientId::kCommerce:
      // Not a switch.
      NOTREACHED();
  }
}

- (void)recordSettingsActionHistogramForAction:
    (ContentNotificationSettingsToggleAction)action {
  base::UmaHistogramEnumeration("ContentNotifications.Settings.Action", action);
}

@end