1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import "ABI47_0_0RCTAlertManager.h" 9 10#import <ABI47_0_0FBReactNativeSpec/ABI47_0_0FBReactNativeSpec.h> 11#import <ABI47_0_0RCTTypeSafety/ABI47_0_0RCTConvertHelpers.h> 12#import <ABI47_0_0React/ABI47_0_0RCTAssert.h> 13#import <ABI47_0_0React/ABI47_0_0RCTConvert.h> 14#import <ABI47_0_0React/ABI47_0_0RCTLog.h> 15#import <ABI47_0_0React/ABI47_0_0RCTUtils.h> 16 17#import "ABI47_0_0CoreModulesPlugins.h" 18#import "ABI47_0_0RCTAlertController.h" 19 20@implementation ABI47_0_0RCTConvert (UIAlertViewStyle) 21 22ABI47_0_0RCT_ENUM_CONVERTER( 23 ABI47_0_0RCTAlertViewStyle, 24 (@{ 25 @"default" : @(ABI47_0_0RCTAlertViewStyleDefault), 26 @"secure-text" : @(ABI47_0_0RCTAlertViewStyleSecureTextInput), 27 @"plain-text" : @(ABI47_0_0RCTAlertViewStylePlainTextInput), 28 @"login-password" : @(ABI47_0_0RCTAlertViewStyleLoginAndPasswordInput), 29 }), 30 ABI47_0_0RCTAlertViewStyleDefault, 31 integerValue) 32 33@end 34 35@implementation ABI47_0_0RCTConvert (UIUserInterfaceStyle) 36 37ABI47_0_0RCT_ENUM_CONVERTER( 38 UIUserInterfaceStyle, 39 (@{ 40 @"unspecified" : @(UIUserInterfaceStyleUnspecified), 41 @"light" : @(UIUserInterfaceStyleLight), 42 @"dark" : @(UIUserInterfaceStyleDark), 43 }), 44 UIUserInterfaceStyleUnspecified, 45 integerValue) 46 47@end 48 49@interface ABI47_0_0RCTAlertManager () <ABI47_0_0NativeAlertManagerSpec> 50 51@end 52 53@implementation ABI47_0_0RCTAlertManager { 54 NSHashTable *_alertControllers; 55} 56 57ABI47_0_0RCT_EXPORT_MODULE() 58 59- (dispatch_queue_t)methodQueue 60{ 61 return dispatch_get_main_queue(); 62} 63 64- (void)invalidate 65{ 66 for (UIAlertController *alertController in _alertControllers) { 67 [alertController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; 68 } 69} 70 71/** 72 * @param {NSDictionary} args Dictionary of the form 73 * 74 * @{ 75 * @"message": @"<Alert message>", 76 * @"buttons": @[ 77 * @{@"<key1>": @"<title1>"}, 78 * @{@"<key2>": @"<title2>"}, 79 * ], 80 * @"cancelButtonKey": @"<key2>", 81 * } 82 * The key from the `buttons` dictionary is passed back in the callback on click. 83 * Buttons are displayed in the order they are specified. 84 */ 85ABI47_0_0RCT_EXPORT_METHOD(alertWithArgs : (ABI47_0_0JS::NativeAlertManager::Args &)args callback : (ABI47_0_0RCTResponseSenderBlock)callback) 86{ 87 NSString *title = [ABI47_0_0RCTConvert NSString:args.title()]; 88 NSString *message = [ABI47_0_0RCTConvert NSString:args.message()]; 89 ABI47_0_0RCTAlertViewStyle type = [ABI47_0_0RCTConvert ABI47_0_0RCTAlertViewStyle:args.type()]; 90 NSArray<NSDictionary *> *buttons = 91 [ABI47_0_0RCTConvert NSDictionaryArray:ABI47_0_0RCTConvertOptionalVecToArray(args.buttons(), ^id(id<NSObject> element) { 92 return element; 93 })]; 94 NSString *defaultValue = [ABI47_0_0RCTConvert NSString:args.defaultValue()]; 95 NSString *cancelButtonKey = [ABI47_0_0RCTConvert NSString:args.cancelButtonKey()]; 96 NSString *destructiveButtonKey = [ABI47_0_0RCTConvert NSString:args.destructiveButtonKey()]; 97 UIKeyboardType keyboardType = [ABI47_0_0RCTConvert UIKeyboardType:args.keyboardType()]; 98 99 if (!title && !message) { 100 ABI47_0_0RCTLogError(@"Must specify either an alert title, or message, or both"); 101 return; 102 } 103 104 if (buttons.count == 0) { 105 if (type == ABI47_0_0RCTAlertViewStyleDefault) { 106 buttons = @[ @{@"0" : ABI47_0_0RCTUIKitLocalizedString(@"OK")} ]; 107 cancelButtonKey = @"0"; 108 } else { 109 buttons = @[ 110 @{@"0" : ABI47_0_0RCTUIKitLocalizedString(@"OK")}, 111 @{@"1" : ABI47_0_0RCTUIKitLocalizedString(@"Cancel")}, 112 ]; 113 cancelButtonKey = @"1"; 114 } 115 } 116 117 ABI47_0_0RCTAlertController *alertController = [ABI47_0_0RCTAlertController alertControllerWithTitle:title 118 message:nil 119 preferredStyle:UIAlertControllerStyleAlert]; 120 121#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ 122 __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 123 if (@available(iOS 13.0, *)) { 124 UIUserInterfaceStyle userInterfaceStyle = [ABI47_0_0RCTConvert UIUserInterfaceStyle:args.userInterfaceStyle()]; 125 alertController.overrideUserInterfaceStyle = userInterfaceStyle; 126 } 127#endif 128 129 switch (type) { 130 case ABI47_0_0RCTAlertViewStylePlainTextInput: { 131 [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 132 textField.secureTextEntry = NO; 133 textField.text = defaultValue; 134 textField.keyboardType = keyboardType; 135 }]; 136 break; 137 } 138 case ABI47_0_0RCTAlertViewStyleSecureTextInput: { 139 [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 140 textField.placeholder = ABI47_0_0RCTUIKitLocalizedString(@"Password"); 141 textField.secureTextEntry = YES; 142 textField.text = defaultValue; 143 textField.keyboardType = keyboardType; 144 }]; 145 break; 146 } 147 case ABI47_0_0RCTAlertViewStyleLoginAndPasswordInput: { 148 [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 149 textField.placeholder = ABI47_0_0RCTUIKitLocalizedString(@"Login"); 150 textField.text = defaultValue; 151 textField.keyboardType = keyboardType; 152 }]; 153 [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 154 textField.placeholder = ABI47_0_0RCTUIKitLocalizedString(@"Password"); 155 textField.secureTextEntry = YES; 156 }]; 157 break; 158 } 159 case ABI47_0_0RCTAlertViewStyleDefault: 160 break; 161 } 162 163 alertController.message = message; 164 165 for (NSDictionary<NSString *, id> *button in buttons) { 166 if (button.count != 1) { 167 ABI47_0_0RCTLogError(@"Button definitions should have exactly one key."); 168 } 169 NSString *buttonKey = button.allKeys.firstObject; 170 NSString *buttonTitle = [ABI47_0_0RCTConvert NSString:button[buttonKey]]; 171 UIAlertActionStyle buttonStyle = UIAlertActionStyleDefault; 172 if ([buttonKey isEqualToString:cancelButtonKey]) { 173 buttonStyle = UIAlertActionStyleCancel; 174 } else if ([buttonKey isEqualToString:destructiveButtonKey]) { 175 buttonStyle = UIAlertActionStyleDestructive; 176 } 177 __weak ABI47_0_0RCTAlertController *weakAlertController = alertController; 178 [alertController 179 addAction:[UIAlertAction 180 actionWithTitle:buttonTitle 181 style:buttonStyle 182 handler:^(__unused UIAlertAction *action) { 183 switch (type) { 184 case ABI47_0_0RCTAlertViewStylePlainTextInput: 185 case ABI47_0_0RCTAlertViewStyleSecureTextInput: 186 callback(@[ buttonKey, [weakAlertController.textFields.firstObject text] ]); 187 [weakAlertController hide]; 188 break; 189 case ABI47_0_0RCTAlertViewStyleLoginAndPasswordInput: { 190 NSDictionary<NSString *, NSString *> *loginCredentials = @{ 191 @"login" : [weakAlertController.textFields.firstObject text], 192 @"password" : [weakAlertController.textFields.lastObject text] 193 }; 194 callback(@[ buttonKey, loginCredentials ]); 195 [weakAlertController hide]; 196 break; 197 } 198 case ABI47_0_0RCTAlertViewStyleDefault: 199 callback(@[ buttonKey ]); 200 [weakAlertController hide]; 201 break; 202 } 203 }]]; 204 } 205 206 if (!_alertControllers) { 207 _alertControllers = [NSHashTable weakObjectsHashTable]; 208 } 209 [_alertControllers addObject:alertController]; 210 211 dispatch_async(dispatch_get_main_queue(), ^{ 212 [alertController show:YES completion:nil]; 213 }); 214} 215 216- (std::shared_ptr<ABI47_0_0facebook::ABI47_0_0React::TurboModule>)getTurboModule: 217 (const ABI47_0_0facebook::ABI47_0_0React::ObjCTurboModule::InitParams &)params 218{ 219 return std::make_shared<ABI47_0_0facebook::ABI47_0_0React::NativeAlertManagerSpecJSI>(params); 220} 221 222@end 223 224Class ABI47_0_0RCTAlertManagerCls(void) 225{ 226 return ABI47_0_0RCTAlertManager.class; 227} 228