diff --git a/Libraries/Utilities/Alert.js b/Libraries/Utilities/Alert.js index 3768be64d9da7d..ee733d1ece63fa 100644 --- a/Libraries/Utilities/Alert.js +++ b/Libraries/Utilities/Alert.js @@ -37,11 +37,10 @@ type Buttons = Array<{ * ## iOS * * On iOS you can specify any number of buttons. Each button can optionally - * specify a style and you can also specify type of the alert. Refer to - * `AlertIOS` for details. + * specify a style, which is one of 'default', 'cancel' or 'destructive'. * * ## Android - * + * * On Android at most three buttons can be specified. Android has a concept * of a neutral, negative and a positive button: * @@ -68,10 +67,15 @@ class Alert { title: ?string, message?: ?string, buttons?: Buttons, - type?: AlertType + type?: AlertType, ): void { if (Platform.OS === 'ios') { - AlertIOS.alert(title, message, buttons, type); + if (typeof type !== 'undefined') { + console.warn('Alert.alert() with a 4th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.'); + AlertIOS.alert(title, message, buttons, type); + return; + } + AlertIOS.alert(title, message, buttons); } else if (Platform.OS === 'android') { AlertAndroid.alert(title, message, buttons); } diff --git a/Libraries/Utilities/AlertIOS.js b/Libraries/Utilities/AlertIOS.js index 2413f38d2ca4f1..ec8c926c6edb15 100644 --- a/Libraries/Utilities/AlertIOS.js +++ b/Libraries/Utilities/AlertIOS.js @@ -12,7 +12,6 @@ 'use strict'; var RCTAlertManager = require('NativeModules').AlertManager; -var invariant = require('invariant'); export type AlertType = $Enum<{ 'default': string; @@ -27,61 +26,156 @@ export type AlertButtonStyle = $Enum<{ 'destructive': string; }>; +type ButtonsArray = Array<{ + text?: string; + onPress?: ?Function; + style?: AlertButtonStyle; +}>; + /** - * Launches an alert dialog with the specified title and message. - * - * Optionally provide a list of buttons. Tapping any button will fire the - * respective onPress callback and dismiss the alert. By default, the only - * button will be an 'OK' button. + * The AlertsIOS utility provides two functions: `alert` and `prompt`. All + * functionality available through `AlertIOS.alert` is also available in the + * cross-platform `Alert.alert`, which we recommend you use if you don't need + * iOS-specific functionality. * - * Use this API for iOS-specific features, such as prompting the user to enter - * some information. In other cases, especially to show static alerts, use - * the cross-platform `Alert` API. + * `AlertIOS.prompt` allows you to prompt the user for input inside of an + * alert popup. * - * ``` - * AlertIOS.alert( - * 'Enter password', - * null, - * [ - * {text: 'Submit', onPress: (text) => console.log('Password: ' + text)}, - * ], - * 'secure-text' - * ) - * ``` */ class AlertIOS { + /** + * Creates a popup to alert the user. See + * [Alert](/react-native/docs/alert.html). + * + * - title: string -- The dialog's title. + * - message: string -- An optional message that appears above the text input. + * - callbackOrButtons -- This optional argument should be either a + * single-argument function or an array of buttons. If passed a function, + * it will be called when the user taps 'OK'. + * + * If passed an array of button configurations, each button should include + * a `text` key, as well as optional `onPress` and `style` keys. + * `style` should be one of 'default', 'cancel' or 'destructive'. + * - type -- *deprecated, do not use* + * + * Example: + * + * ``` + * AlertIOS.alert( + * 'Sync Complete', + * 'All your data are belong to us.' + * ); + * ``` + */ static alert( title: ?string, message?: ?string, - buttons?: Array<{ - text?: string; - onPress?: ?Function; - style?: AlertButtonStyle; - }>, - type?: ?AlertType + callbackOrButtons?: ?(() => void) | ButtonsArray, + type?: AlertType, ): void { + if (typeof type !== 'undefined') { + console.warn('AlertIOS.alert() with a 4th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.'); + this.prompt(title, message, callbackOrButtons, type); + return; + } + this.prompt(title, message, callbackOrButtons, 'default'); + } + + /** + * Prompt the user to enter some text. + * + * - title: string -- The dialog's title. + * - message: string -- An optional message that appears above the text input. + * - callbackOrButtons -- This optional argument should be either a + * single-argument function or an array of buttons. If passed a function, + * it will be called with the prompt's value when the user taps 'OK'. + * + * If passed an array of button configurations, each button should include + * a `text` key, as well as optional `onPress` and `style` keys (see example). + * `style` should be one of 'default', 'cancel' or 'destructive'. + * - type: string -- This configures the text input. One of 'plain-text', + * 'secure-text' or 'login-password'. + * - defaultValue: string -- the default value for the text field. + * + * Example with custom buttons: + * ``` + * AlertIOS.prompt( + * 'Enter password', + * 'Enter your password to claim your $1.5B in lottery winnings', + * [ + * {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, + * {text: 'OK', onPress: password => console.log('OK Pressed, password: ' + password)}, + * ], + * 'secure-text' + * ); + * ``` + * + * Example with the default button and a custom callback: + * ``` + * AlertIOS.prompt( + * 'Update username', + * null, + * text => console.log("Your username is "+text), + * null, + * 'default' + * ) + * ``` + */ + static prompt( + title: ?string, + message?: ?string, + callbackOrButtons?: ?((text: string) => void) | ButtonsArray, + type?: ?AlertType = 'plain-text', + defaultValue?: string, + ): void { + if (typeof type === 'function') { + console.warn( + 'You passed a callback function as the "type" argument to AlertIOS.prompt(). React Native is ' + + 'assuming you want to use the deprecated AlertIOS.prompt(title, defaultValue, buttons, callback) ' + + 'signature. The current signature is AlertIOS.prompt(title, message, callbackOrButtons, type, defaultValue) ' + + 'and the old syntax will be removed in a future version.'); + + var callback = type; + var defaultValue = message; + RCTAlertManager.alertWithArgs({ + title: title || undefined, + type: 'plain-text', + defaultValue, + }, (id, value) => { + callback(value); + }); + return; + } + var callbacks = []; - var buttonsSpec = []; + var buttons = []; var cancelButtonKey; var destructiveButtonKey; - buttons && buttons.forEach((btn, index) => { - callbacks[index] = btn.onPress; - if (btn.style == 'cancel') { - cancelButtonKey = String(index); - } else if (btn.style == 'destructive') { - destructiveButtonKey = String(index); - } - if (btn.text || index < (buttons || []).length - 1) { - var btnDef = {}; - btnDef[index] = btn.text || ''; - buttonsSpec.push(btnDef); - } - }); + if (typeof callbackOrButtons === 'function') { + callbacks = [callbackOrButtons]; + } + else if (callbackOrButtons instanceof Array) { + callbackOrButtons.forEach((btn, index) => { + callbacks[index] = btn.onPress; + if (btn.style === 'cancel') { + cancelButtonKey = String(index); + } else if (btn.style === 'destructive') { + destructiveButtonKey = String(index); + } + if (btn.text || index < (callbackOrButtons || []).length - 1) { + var btnDef = {}; + btnDef[index] = btn.text || ''; + buttons.push(btnDef); + } + }); + } + RCTAlertManager.alertWithArgs({ title: title || undefined, message: message || undefined, - buttons: buttonsSpec, + buttons, type: type || undefined, + defaultValue, cancelButtonKey, destructiveButtonKey, }, (id, value) => { @@ -89,43 +183,6 @@ class AlertIOS { cb && cb(value); }); } - - /** - * Prompt the user to enter some text. - */ - static prompt( - title: string, - value?: string, - buttons?: Array<{ - text?: string; - onPress?: ?Function; - style?: AlertButtonStyle; - }>, - callback?: ?Function - ): void { - if (arguments.length === 2) { - if (typeof value === 'object') { - buttons = value; - value = undefined; - } else if (typeof value === 'function') { - callback = value; - value = undefined; - } - } else if (arguments.length === 3 && typeof buttons === 'function') { - callback = buttons; - buttons = undefined; - } - - invariant( - !(callback && buttons) && (callback || buttons), - 'Must provide either a button list or a callback, but not both' - ); - - if (!buttons) { - buttons = [{ onPress: callback }]; - } - this.alert(title, value, buttons, 'plain-text'); - } } module.exports = AlertIOS; diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m index d735d54423a26b..b2e7fe0a3ae055 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -75,6 +75,7 @@ - (void)invalidate NSString *message = [RCTConvert NSString:args[@"message"]]; UIAlertViewStyle type = [RCTConvert UIAlertViewStyle:args[@"type"]]; NSArray *buttons = [RCTConvert NSDictionaryArray:args[@"buttons"]]; + NSString *defaultValue = [RCTConvert NSString:args[@"defaultValue"]]; NSString *cancelButtonKey = [RCTConvert NSString:args[@"cancelButtonKey"]]; NSString *destructiveButtonKey = [RCTConvert NSString:args[@"destructiveButtonKey"]]; @@ -111,6 +112,10 @@ - (void)invalidate alertView.alertViewStyle = type; alertView.message = message; + if (type != UIAlertViewStyleDefault) { + [alertView textFieldAtIndex:0].text = defaultValue; + } + NSMutableArray *buttonKeys = [[NSMutableArray alloc] initWithCapacity:buttons.count];