// 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/overlays/ui_bundled/web_content_area/alerts/alert_overlay_mediator.h"
#import <string_view>
#import "base/check_op.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "ios/chrome/browser/alert_view/ui_bundled/alert_action.h"
#import "ios/chrome/browser/alert_view/ui_bundled/alert_consumer.h"
#import "ios/chrome/browser/overlays/model/public/overlay_callback_manager.h"
#import "ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h"
using alert_overlays::AlertRequest;
using alert_overlays::AlertResponse;
@interface AlertOverlayMediator ()
// Returns the alert request config from the OverlayRequest used to initialize
// the mediator.
@property(nonatomic, readonly) AlertRequest* alertRequest;
// Returns the alert actions to provide to the consumer. Constructed using the
// AlertRequest.
@property(nonatomic, readonly) NSArray<NSArray<AlertAction*>*>* alertActions;
// Returns an array containing the current values of all alert text fields.
@property(nonatomic, readonly) NSArray<NSString*>* textFieldValues;
@end
@implementation AlertOverlayMediator
#pragma mark - Accessors
- (void)setConsumer:(id<AlertConsumer>)consumer {
if (_consumer == consumer)
return;
_consumer = consumer;
if (!self.request)
return;
AlertRequest* alertRequest = self.alertRequest;
DCHECK(alertRequest);
[_consumer setTitle:alertRequest->title()];
[_consumer setMessage:alertRequest->message()];
[_consumer setTextFieldConfigurations:alertRequest->text_field_configs()];
NSArray<NSArray<AlertAction*>*>* alertActions = self.alertActions;
DCHECK_GT(alertActions.count, 0U);
[_consumer setActions:alertActions];
[_consumer
setAlertAccessibilityIdentifier:alertRequest->accessibility_identifier()];
}
- (AlertRequest*)alertRequest {
return self.request ? self.request->GetConfig<AlertRequest>() : nullptr;
}
- (NSArray<NSArray<AlertAction*>*>*)alertActions {
AlertRequest* alertRequest = self.alertRequest;
if (!alertRequest || !alertRequest->button_configs().size())
return nil;
const std::vector<std::vector<alert_overlays::ButtonConfig>>& buttonConfigs =
alertRequest->button_configs();
size_t rowCount = buttonConfigs.size();
NSMutableArray<NSArray<AlertAction*>*>* actions =
[[NSMutableArray<NSArray<AlertAction*>*> alloc]
initWithCapacity:rowCount];
for (size_t i = 0; i < rowCount; ++i) {
const std::vector<alert_overlays::ButtonConfig> buttonConfigsForRow =
buttonConfigs[i];
size_t columnCount = buttonConfigsForRow.size();
DCHECK_GT(columnCount, 0U);
NSMutableArray<AlertAction*>* actionsForRow =
[[NSMutableArray<AlertAction*> alloc] initWithCapacity:columnCount];
for (size_t j = 0; j < columnCount; ++j) {
[actionsForRow
addObject:[AlertAction
actionWithTitle:buttonConfigsForRow[j].title
style:buttonConfigsForRow[j].style
handler:[self actionForButtonAtIndexRow:i
column:j]]];
}
[actions addObject:actionsForRow];
}
return actions;
}
- (NSArray<NSString*>*)textFieldValues {
AlertRequest* alertRequest = self.alertRequest;
if (!alertRequest || !alertRequest->text_field_configs().count)
return nil;
NSUInteger textFieldCount = alertRequest->text_field_configs().count;
NSMutableArray<NSString*>* textFieldValues =
[[NSMutableArray<NSString*> alloc] initWithCapacity:textFieldCount];
for (NSUInteger i = 0; i < textFieldCount; ++i) {
NSString* textFieldValue = [self.dataSource textFieldInputForMediator:self
textFieldIndex:i];
[textFieldValues addObject:textFieldValue ?: @""];
}
return textFieldValues;
}
#pragma mark - OverlayRequestMediator
+ (const OverlayRequestSupport*)requestSupport {
return AlertRequest::RequestSupport();
}
#pragma mark - Private
// Sets a completion OverlayResponse after the button at `tappedButtonIndexRow`
// and `tappedButtonIndexColumn` was tapped.
- (void)setCompletionResponseWithRow:(size_t)tappedButtonIndexRow
column:(size_t)tappedButtonIndexColumn {
AlertRequest* alertRequest = self.alertRequest;
if (!alertRequest)
return;
std::unique_ptr<OverlayResponse> alertResponse =
OverlayResponse::CreateWithInfo<AlertResponse>(
tappedButtonIndexRow, tappedButtonIndexColumn, self.textFieldValues);
self.request->GetCallbackManager()->SetCompletionResponse(
alertRequest->response_converter().Run(std::move(alertResponse)));
// The response converter should convert the AlertResponse into a feature-
// specific OverlayResponseInfo type.
OverlayResponse* convertedResponse =
self.request->GetCallbackManager()->GetCompletionResponse();
DCHECK(!convertedResponse || !convertedResponse->GetInfo<AlertResponse>());
}
// Returns the action block for the button at index `row` and `column`.
- (void (^)(AlertAction* action))actionForButtonAtIndexRow:(size_t)row
column:(size_t)column {
__weak __typeof__(self) weakSelf = self;
std::string_view actionName =
self.alertRequest->button_configs()[row][column].user_action_name;
return ^(AlertAction*) {
if (!actionName.empty()) {
base::RecordComputedAction(actionName.data());
}
__typeof__(self) strongSelf = weakSelf;
[strongSelf setCompletionResponseWithRow:row column:column];
[strongSelf.delegate stopOverlayForMediator:strongSelf];
};
}
@end