chromium/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IOS_CHROME_BROWSER_OVERLAYS_MODEL_PUBLIC_WEB_CONTENT_AREA_ALERT_OVERLAY_H_
#define IOS_CHROME_BROWSER_OVERLAYS_MODEL_PUBLIC_WEB_CONTENT_AREA_ALERT_OVERLAY_H_

#import <UIKit/UIKit.h>

#include <memory>
#include <string_view>
#include <vector>

#include "base/functional/callback.h"
#include "ios/chrome/browser/overlays/model/public/overlay_request_config.h"
#include "ios/chrome/browser/overlays/model/public/overlay_response_info.h"

class OverlayResponse;
@class TextFieldConfiguration;

namespace alert_overlays {

// Overlays that use alert views should construct an AlertRequest as auxiliary
// data for their feature-specific OverlayRequestConfig.  This allows a single
// UI implementation to be shared across all alert overlays while encapsulating
// this UI implementation knowledge from model code that creates OverlayRequests
// or observes their presentation.
//
// Usage example:
//
// --- in foo_overlay.h ---
// class FooRequest : public OverlayRequestConfig<FooRequest> {
//  public:
//   // ... expose Foo overlay configuration data ...
//  private:
//   void CreateAuxiliaryData(base::SupportsUserData* user_data) override;
// };
//
// class FooResponse : public OverlayResponseInfo<FooResponse> {
//  public:
//   // ... expose Foo overlay user interaction data ...
// };
//
// --- in foo_overlay.mm ---
// std::unique_ptr<OverlayResponse> CreateFooResponse(
//     std::unique_ptr<OverlayResponse> response) {
//   AlertResponse* alert_response = response->GetInfo<AlertResponse>();
//   return OverlayResponse::CreateWithInfo<FooResponse>(
//       /* ... parse `alert_response` to create a FooResponse ... */);
// }
//
// void FooRequest::CreateAuxiliaryData(base::SupportsUserData* user_data) {
//   AlertRequest::CreateForUserData(
//       user_data, /* ... use Foo data to populate alert ... */,
//       base::BindRepeating(&CreateFooResponse));
// }

// Callback that converts an OverlayResponse created with an AlertResponse
// to one exposing its feature-specific logic.  For example, a feature asking
// for the email of a user with an alert can convert `alert_response` to an
// OverlayResponse created with an OverlayResponseInfo that exposes the email
// typed into a text field in the alert, rather than requiring the overlay
// requester to parse the AlertResponse's text field values.
typedef base::RepeatingCallback<std::unique_ptr<OverlayResponse>(
    std::unique_ptr<OverlayResponse> alert_response)>
    ResponseConverter;

// Config struct used to set up buttons shown in overlay UI for an AlertRequest.
struct ButtonConfig {
  // Creates a ButtonConfig with `title` and `style`.
  explicit ButtonConfig(NSString* title,
                        UIAlertActionStyle style = UIAlertActionStyleDefault);
  // Creates a ButtonConfig with `title` and `style`. UMA User Action with
  // `user_action_name` will be recorded when this button is tapped.
  ButtonConfig(NSString* title,
               std::string_view user_action_name,
               UIAlertActionStyle style = UIAlertActionStyleDefault);
  ButtonConfig(const ButtonConfig&);
  ButtonConfig() = delete;
  ~ButtonConfig();
  // The button's title.
  NSString* title;
  // Name of UMA User Action to log when the button is tapped.
  std::string_view user_action_name;
  // The button's style.
  UIAlertActionStyle style;
};

// Configuration object for OverlayRequests implemented using alert views.  This
// config should only be instantiated as auxiliary data to feature-specific
// configs that are implemented using alert views.  It should not be used with
// OverlayRequest::CreateWithConfig(), as it exposes UI implementation details
// to model code that requests or observes overlay presentation.
class AlertRequest : public OverlayRequestConfig<AlertRequest> {
 public:
  ~AlertRequest() override;

  // The alert's title.  All AlertRequests must have a title, message, or both.
  NSString* title() const { return title_; }
  // The alert's message.  All AlertRequests must have a title, message, or
  // both.
  NSString* message() const { return message_; }
  // The accessibility identifier to use for the alert view.  Can be nil.
  NSString* accessibility_identifier() const {
    return accessibility_identifier_;
  }
  // The alert's text field configurations.  Can be nil for alerts without text
  // fields.
  NSArray<TextFieldConfiguration*>* text_field_configs() const {
    return text_field_configs_;
  }
  // The button styles, titles and placement, with each child vector being a
  // horizontal list of buttons.  All alerts must have at least one button.
  const std::vector<std::vector<ButtonConfig>>& button_configs() const {
    return button_configs_;
  }
  // Callback that converts an alert-specific OverlayResponse to one exposing
  // its feature-specific logic.
  const ResponseConverter& response_converter() const {
    return response_converter_;
  }

 private:
  OVERLAY_USER_DATA_SETUP(AlertRequest);

  // Constructor called by CreateForUserData(). All arguments are copied to the
  // ivars below.  `title`, `message`, or both must be non-empty strings.
  // `button_configs` must contain at least one ButtonConfig.
  // `response_converter` must be non-null.
  AlertRequest(NSString* title,
               NSString* message,
               NSString* accessibility_identifier,
               NSArray<TextFieldConfiguration*>* text_field_configs,
               const std::vector<std::vector<ButtonConfig>>& button_configs,
               ResponseConverter response_converter);

  NSString* title_ = nil;
  NSString* message_ = nil;
  NSString* accessibility_identifier_ = nil;
  NSArray<TextFieldConfiguration*>* text_field_configs_ = nil;
  const std::vector<std::vector<ButtonConfig>> button_configs_;
  ResponseConverter response_converter_;
};

// Response info used to create completion OverlayResponses for alert overlays.
class AlertResponse : public OverlayResponseInfo<AlertResponse> {
 public:
  ~AlertResponse() override;

  // The row index of the button tapped by the user to close the dialog.
  size_t tapped_button_row_index() const { return tapped_button_row_index_; }
  // The column index of the button tapped by the user to close the dialog.
  size_t tapped_button_column_index() const {
    return tapped_button_column_index_;
  }
  // The values of the text fields when the button was tapped.
  NSArray<NSString*>* text_field_values() const { return text_field_values_; }

 private:
  OVERLAY_USER_DATA_SETUP(AlertResponse);
  AlertResponse(size_t tapped_button_row_index,
                size_t tapped_button_column_index,
                NSArray<NSString*>* text_field_values);

  const size_t tapped_button_row_index_ = 0;
  const size_t tapped_button_column_index_ = 0;
  NSArray<NSString*>* text_field_values_ = nil;
};

}  // alert_overlays

#endif  // IOS_CHROME_BROWSER_OVERLAYS_MODEL_PUBLIC_WEB_CONTENT_AREA_ALERT_OVERLAY_H_