From ef339250b582e345ac25db1cadd0489b39292b56 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 11 May 2015 16:40:04 -0700 Subject: [PATCH] [ReactNative] Add prompt to AlertIOS Summary: Add `AlertIOS.prompt` It's compatible with the js spec, with the exception that I had to add a callback param since it's async. Also supports the same button configuration as `AlertIOS.alert`. @public Test Plan: I've updated the `AlertIOS` example on UIExplorer with every valid combination of parameters, so just going through it should be fine. --- Examples/UIExplorer/AlertIOSExample.js | 91 +++++++++++++++++++++++++- Libraries/Utilities/AlertIOS.js | 50 ++++++++++++-- React/Modules/RCTAlertManager.m | 20 +++++- 3 files changed, 154 insertions(+), 7 deletions(-) diff --git a/Examples/UIExplorer/AlertIOSExample.js b/Examples/UIExplorer/AlertIOSExample.js index a25a319e7d..2d57fd5fe8 100644 --- a/Examples/UIExplorer/AlertIOSExample.js +++ b/Examples/UIExplorer/AlertIOSExample.js @@ -95,9 +95,98 @@ exports.examples = [{ ); - }, + } +}, +{ + title: 'Prompt', + render(): React.Component { + return + } }]; +class PromptExample extends React.Component { + constructor(props) { + super(props); + + this.promptResponse = this.promptResponse.bind(this); + this.state = { + promptValue: undefined, + }; + + this.title = 'Type a value'; + this.defaultValue = 'Default value'; + this.buttons = [{ + text: 'Custom cancel', + }, { + text: 'Custom OK', + onPress: this.promptResponse + }]; + } + + render() { + return ( + + + Prompt value: {this.state.promptValue} + + + + + + + prompt with title & callback + + + + + + + + + prompt with title & custom buttons + + + + + + + + + prompt with title, default value & callback + + + + + + + + + prompt with title, default value & custom buttons + + + + + ); + } + + prompt() { + // Flow's apply support is broken: #7035621 + ((AlertIOS.prompt: any).apply: any)(AlertIOS, arguments); + } + + promptResponse(promptValue) { + this.setState({ promptValue }); + } +} + var styles = StyleSheet.create({ wrapper: { borderRadius: 5, diff --git a/Libraries/Utilities/AlertIOS.js b/Libraries/Utilities/AlertIOS.js index 0d1612bc9e..7fd15d254a 100644 --- a/Libraries/Utilities/AlertIOS.js +++ b/Libraries/Utilities/AlertIOS.js @@ -12,6 +12,7 @@ 'use strict'; var RCTAlertManager = require('NativeModules').AlertManager; +var invariant = require('invariant'); var DEFAULT_BUTTON_TEXT = 'OK'; var DEFAULT_BUTTON = { @@ -47,14 +48,17 @@ class AlertIOS { message?: ?string, buttons?: Array<{ text: ?string; - onPress: ?Function; - }> + onPress?: ?Function; + }>, + type?: ?string ): void { var callbacks = []; var buttonsSpec = []; title = title || ''; message = message || ''; buttons = buttons || [DEFAULT_BUTTON]; + type = type || ''; + buttons.forEach((btn, index) => { callbacks[index] = btn.onPress; var btnDef = {}; @@ -65,12 +69,50 @@ class AlertIOS { title, message, buttons: buttonsSpec, - }, (id) => { + type, + }, (id, value) => { var cb = callbacks[id]; - cb && cb(); + cb && cb(value); }); } + static prompt( + title: string, + value?: string, + buttons?: Array<{ + text: ?string; + onPress?: ?Function; + }>, + 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 = [{ + text: 'Cancel', + }, { + text: 'OK', + 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 2690de1dfe..3bdb035fc4 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -59,6 +59,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args { NSString *title = args[@"title"]; NSString *message = args[@"message"]; + NSString *type = args[@"type"]; NSArray *buttons = args[@"buttons"]; if (!title && !message) { @@ -70,13 +71,20 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args } UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title - message:message + message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil]; NSMutableArray *buttonKeys = [[NSMutableArray alloc] initWithCapacity:buttons.count]; + if ([type isEqualToString:@"plain-text"]) { + alertView.alertViewStyle = UIAlertViewStylePlainTextInput; + [alertView textFieldAtIndex:0].text = message; + } else { + alertView.message = message; + } + NSInteger index = 0; for (NSDictionary *button in buttons) { if (button.count != 1) { @@ -108,7 +116,15 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args RCTResponseSenderBlock callback = _alertCallbacks[index]; NSArray *buttonKeys = _alertButtonKeys[index]; - callback(@[buttonKeys[buttonIndex]]); + NSArray *args; + + if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput) { + args = @[buttonKeys[buttonIndex], [alertView textFieldAtIndex:0].text]; + } else { + args = @[buttonKeys[buttonIndex]]; + } + + callback(args); [_alerts removeObjectAtIndex:index]; [_alertCallbacks removeObjectAtIndex:index];