From 48c052de3830ab5afe344a61bec5ade9dd2c6df6 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Thu, 4 Nov 2021 09:15:57 +0000 Subject: [PATCH 1/2] Add support for "preferred" AlertButtonStyle See preferredAction on UIAlertController https://developer.apple.com/documentation/uikit/uialertcontroller/ --- Libraries/Alert/Alert.js | 10 +++- Libraries/Alert/NativeAlertManager.js | 1 + React/CoreModules/RCTAlertManager.mm | 56 ++++++++++--------- .../js/examples/Alert/AlertExample.js | 44 ++++++++++++++- 4 files changed, 84 insertions(+), 27 deletions(-) diff --git a/Libraries/Alert/Alert.js b/Libraries/Alert/Alert.js index 091ff74ab654..0f81a4f6154a 100644 --- a/Libraries/Alert/Alert.js +++ b/Libraries/Alert/Alert.js @@ -17,7 +17,11 @@ export type AlertType = | 'plain-text' | 'secure-text' | 'login-password'; -export type AlertButtonStyle = 'default' | 'cancel' | 'destructive'; +export type AlertButtonStyle = + | 'default' + | 'cancel' + | 'destructive' + | 'preferred'; export type Buttons = Array<{ text?: string, onPress?: ?Function, @@ -113,6 +117,7 @@ class Alert { const buttons = []; let cancelButtonKey; let destructiveButtonKey; + let preferredButtonKey; if (typeof callbackOrButtons === 'function') { callbacks = [callbackOrButtons]; } else if (Array.isArray(callbackOrButtons)) { @@ -122,6 +127,8 @@ class Alert { cancelButtonKey = String(index); } else if (btn.style === 'destructive') { destructiveButtonKey = String(index); + } else if (btn.style === 'preferred') { + preferredButtonKey = String(index); } if (btn.text || index < (callbackOrButtons || []).length - 1) { const btnDef = {}; @@ -140,6 +147,7 @@ class Alert { defaultValue, cancelButtonKey, destructiveButtonKey, + preferredButtonKey, keyboardType, }, (id, value) => { diff --git a/Libraries/Alert/NativeAlertManager.js b/Libraries/Alert/NativeAlertManager.js index ec01b4a916e9..11d7ed6b4d0a 100644 --- a/Libraries/Alert/NativeAlertManager.js +++ b/Libraries/Alert/NativeAlertManager.js @@ -19,6 +19,7 @@ export type Args = {| defaultValue?: string, cancelButtonKey?: string, destructiveButtonKey?: string, + preferredButtonKey?: string, keyboardType?: string, |}; diff --git a/React/CoreModules/RCTAlertManager.mm b/React/CoreModules/RCTAlertManager.mm index e0f450e480e7..5d2f2400cb1e 100644 --- a/React/CoreModules/RCTAlertManager.mm +++ b/React/CoreModules/RCTAlertManager.mm @@ -80,6 +80,7 @@ - (void)invalidate NSString *defaultValue = [RCTConvert NSString:args.defaultValue()]; NSString *cancelButtonKey = [RCTConvert NSString:args.cancelButtonKey()]; NSString *destructiveButtonKey = [RCTConvert NSString:args.destructiveButtonKey()]; + NSString *preferredButtonKey = [RCTConvert NSString:args.preferredButtonKey()]; UIKeyboardType keyboardType = [RCTConvert UIKeyboardType:args.keyboardType()]; if (!title && !message) { @@ -152,32 +153,37 @@ - (void)invalidate buttonStyle = UIAlertActionStyleDestructive; } __weak RCTAlertController *weakAlertController = alertController; + + UIAlertAction *action = [UIAlertAction actionWithTitle:buttonTitle + style:buttonStyle + handler:^(__unused UIAlertAction *action) { + switch (type) { + case RCTAlertViewStylePlainTextInput: + case RCTAlertViewStyleSecureTextInput: + callback(@[ buttonKey, [weakAlertController.textFields.firstObject text] ]); + [weakAlertController hide]; + break; + case RCTAlertViewStyleLoginAndPasswordInput: { + NSDictionary *loginCredentials = @{ + @"login" : [weakAlertController.textFields.firstObject text], + @"password" : [weakAlertController.textFields.lastObject text] + }; + callback(@[ buttonKey, loginCredentials ]); + [weakAlertController hide]; + break; + } + case RCTAlertViewStyleDefault: + callback(@[ buttonKey ]); + [weakAlertController hide]; + break; + } + }]; [alertController - addAction:[UIAlertAction - actionWithTitle:buttonTitle - style:buttonStyle - handler:^(__unused UIAlertAction *action) { - switch (type) { - case RCTAlertViewStylePlainTextInput: - case RCTAlertViewStyleSecureTextInput: - callback(@[ buttonKey, [weakAlertController.textFields.firstObject text] ]); - [weakAlertController hide]; - break; - case RCTAlertViewStyleLoginAndPasswordInput: { - NSDictionary *loginCredentials = @{ - @"login" : [weakAlertController.textFields.firstObject text], - @"password" : [weakAlertController.textFields.lastObject text] - }; - callback(@[ buttonKey, loginCredentials ]); - [weakAlertController hide]; - break; - } - case RCTAlertViewStyleDefault: - callback(@[ buttonKey ]); - [weakAlertController hide]; - break; - } - }]]; + addAction:action]; + + if ([buttonKey isEqualToString:preferredButtonKey]) { + [alertController setPreferredAction:action]; + } } if (!_alertControllers) { diff --git a/packages/rn-tester/js/examples/Alert/AlertExample.js b/packages/rn-tester/js/examples/Alert/AlertExample.js index d17f8ed93573..08d3bcaad4eb 100644 --- a/packages/rn-tester/js/examples/Alert/AlertExample.js +++ b/packages/rn-tester/js/examples/Alert/AlertExample.js @@ -190,6 +190,39 @@ const AlertWithStyles = () => { ); }; +const AlertWithStylesPreferred = () => { + const [message, setMessage] = useState(''); + + const alertMessage = + "The OK button is styled with 'preferred', so it is emphasized over the cancel button."; + + return ( + + + Alert.alert('Foo Title', alertMessage, [ + { + text: 'OK', + style: 'preferred', + onPress: () => setMessage('OK Pressed!'), + }, + { + text: 'Cancel', + style: 'cancel', + onPress: () => setMessage('Cancel Pressed!'), + }, + ]) + }> + + Tap to view alert + + + + + ); +}; + const styles = StyleSheet.create({ wrapper: { borderRadius: 5, @@ -257,9 +290,18 @@ exports.examples = [ title: 'Alert with styles', platform: 'ios', description: - "Alert buttons can be styled. There are three button styles - 'default' | 'cancel' | 'destructive'.", + "Alert buttons can be styled. Three button button styles are shown here - 'default' | 'cancel' | 'destructive'.", render(): React.Node { return ; }, }, + { + title: 'Alert with styles + preferred', + platform: 'ios', + description: + "Alert buttons can be styled. Setting the style of a button as 'preferred' will give it emphasis over cancel buttons", + render(): React.Node { + return ; + }, + }, ]; From 4394989a119b40bcf7b10d9e002b52b16252c6a9 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 10 Nov 2021 15:26:26 +0000 Subject: [PATCH 2/2] Moved "preferred" AlertButtonStyle to a separate flag "isPreferred" --- Libraries/Alert/Alert.js | 9 +++------ packages/rn-tester/js/examples/Alert/AlertExample.js | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Libraries/Alert/Alert.js b/Libraries/Alert/Alert.js index 0f81a4f6154a..da713ac46101 100644 --- a/Libraries/Alert/Alert.js +++ b/Libraries/Alert/Alert.js @@ -17,14 +17,11 @@ export type AlertType = | 'plain-text' | 'secure-text' | 'login-password'; -export type AlertButtonStyle = - | 'default' - | 'cancel' - | 'destructive' - | 'preferred'; +export type AlertButtonStyle = 'default' | 'cancel' | 'destructive'; export type Buttons = Array<{ text?: string, onPress?: ?Function, + isPreferred?: boolean, style?: AlertButtonStyle, ... }>; @@ -127,7 +124,7 @@ class Alert { cancelButtonKey = String(index); } else if (btn.style === 'destructive') { destructiveButtonKey = String(index); - } else if (btn.style === 'preferred') { + } else if (btn.isPreferred) { preferredButtonKey = String(index); } if (btn.text || index < (callbackOrButtons || []).length - 1) { diff --git a/packages/rn-tester/js/examples/Alert/AlertExample.js b/packages/rn-tester/js/examples/Alert/AlertExample.js index 08d3bcaad4eb..ebad451d9be8 100644 --- a/packages/rn-tester/js/examples/Alert/AlertExample.js +++ b/packages/rn-tester/js/examples/Alert/AlertExample.js @@ -204,7 +204,7 @@ const AlertWithStylesPreferred = () => { Alert.alert('Foo Title', alertMessage, [ { text: 'OK', - style: 'preferred', + isPreferred: 'preferred', onPress: () => setMessage('OK Pressed!'), }, { @@ -290,7 +290,7 @@ exports.examples = [ title: 'Alert with styles', platform: 'ios', description: - "Alert buttons can be styled. Three button button styles are shown here - 'default' | 'cancel' | 'destructive'.", + "Alert buttons can be styled. There are three button styles - 'default' | 'cancel' | 'destructive'.", render(): React.Node { return ; }, @@ -299,7 +299,7 @@ exports.examples = [ title: 'Alert with styles + preferred', platform: 'ios', description: - "Alert buttons can be styled. Setting the style of a button as 'preferred' will give it emphasis over cancel buttons", + "Alert buttons with 'isPreferred' will be emphasized, even over cancel buttons", render(): React.Node { return ; },