| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 | 
							- // @flow
 - 
 - import _ from 'lodash';
 - import React from 'react';
 - import { Modal, StyleSheet, TextInput } from 'react-native';
 - import Prompt from 'react-native-prompt';
 - import { connect } from 'react-redux';
 - 
 - import { translate } from '../../i18n';
 - import { LoadingIndicator } from '../../react';
 - import { set } from '../../redux';
 - 
 - import AbstractDialog from './AbstractDialog';
 - import type {
 -     Props as AbstractDialogProps,
 -     State as AbstractDialogState
 - } from './AbstractDialog';
 - import { dialog as styles } from './styles';
 - 
 - /**
 -  * The value of the style property {@link _TAG_KEY} which identifies the
 -  * OK/submit button of {@code Prompt}.
 -  */
 - const _SUBMIT_TEXT_TAG_VALUE = '_SUBMIT_TEXT_TAG_VALUE';
 - 
 - /**
 -  * The name of the style property which identifies ancestors of {@code Prompt}
 -  * such as its OK/submit button for the purposes of workarounds implemented by
 -  * {@code Dialog}.
 -  *
 -  * XXX The value may trigger a react-native warning in the Debug configuration
 -  * but, unfortunately, I couldn't find a value that wouldn't.
 -  */
 - const _TAG_KEY = '_TAG_KEY';
 - 
 - /**
 -  * The type of the React {@code Component} props of {@link Dialog}.
 -  */
 - type Props = {
 -     ...AbstractDialogProps,
 - 
 -     /**
 -      * I18n key to put as body title.
 -      */
 -     bodyKey: string,
 - 
 -     textInputProps: Object
 - };
 - 
 - /**
 -  * The type of the React {@code Component} state of {@link Dialog}.
 -  */
 - type State = {
 -     ...AbstractDialogState,
 - 
 -     /**
 -      * The text of the {@link TextInput} rendered by {@link Prompt} in
 -      * general and by this {@code Dialog} in particular if no
 -      * {@code children} are specified to it. It mimics/reimplements the
 -      * functionality of {@code Prompt} because this {@code Dialog} does not
 -      * really render the (whole) {@code Prompt}.
 -      */
 -     text: string
 - };
 - 
 - /**
 -  * Implements {@code AbstractDialog} on react-native using {@code Prompt}.
 -  */
 - class Dialog extends AbstractDialog<Props, State> {
 -     state = {
 -         text: ''
 -     };
 - 
 -     /**
 -      * Initailizes a new {@code Dialog} instance.
 -      *
 -      * @param {Object} props - The read-only React {@code Component} props with
 -      * which the new instance is to be initialized.
 -      */
 -     constructor(props: Object) {
 -         super(props);
 - 
 -         // Bind event handlers so they are only bound once per instance.
 -         this._onChangeText = this._onChangeText.bind(this);
 -         this._onSubmit = this._onSubmit.bind(this);
 -     }
 - 
 -     /**
 -      * Implements React's {@link Component#render()}.
 -      *
 -      * @inheritdoc
 -      * @returns {ReactElement}
 -      */
 -     render() {
 -         const {
 -             bodyKey,
 -             cancelDisabled,
 -             cancelTitleKey = 'dialog.Cancel',
 -             okDisabled,
 -             okTitleKey = 'dialog.Ok',
 -             t /* XXX The following silences flow errors: */ = _.identity,
 -             titleKey,
 -             titleString
 -         } = this.props;
 - 
 -         const cancelButtonTextStyle
 -             = cancelDisabled ? styles.disabledButtonText : styles.buttonText;
 -         let submitButtonTextStyle
 -             = okDisabled ? styles.disabledButtonText : styles.buttonText;
 - 
 -         submitButtonTextStyle = {
 -             ...submitButtonTextStyle,
 -             [_TAG_KEY]: _SUBMIT_TEXT_TAG_VALUE
 -         };
 - 
 -         let el: ?React$Element<*> = (
 -             <Prompt
 -                 cancelButtonTextStyle = { cancelButtonTextStyle }
 -                 cancelText = { t(cancelTitleKey) }
 -                 defaultValue = { this.state.text }
 -                 onCancel = { this._onCancel }
 -                 onChangeText = { this._onChangeText }
 -                 onSubmit = { this._onSubmit }
 -                 placeholder = { t(bodyKey) }
 -                 submitButtonTextStyle = { submitButtonTextStyle }
 -                 submitText = { t(okTitleKey) }
 -                 textInputProps = { this.props.textInputProps }
 -                 title = { titleString || t(titleKey) }
 -                 visible = { true } />
 -         );
 - 
 -         // XXX The following implements workarounds with knowledge of
 -         // react-native-prompt/Prompt's implementation.
 - 
 -         if (el) {
 -             // eslint-disable-next-line new-cap, no-extra-parens
 -             el = (new (el.type)(el.props)).render();
 -         }
 - 
 -         let { children } = this.props;
 - 
 -         children = React.Children.count(children) ? children : undefined;
 - 
 -         // eslint-disable-next-line no-shadow
 -         el = this._mapReactElement(el, (el: React$Element<*>) => {
 -             const { type } = el;
 - 
 -             if (type === Modal) {
 -                 // * Modal handles hardware button presses for back navigation.
 -                 //   Firstly, we don't want Prompt's default behavior to merely
 -                 //   hide the Modal - we want this Dialog to be canceled.
 -                 //   Secondly, we cannot get Prompt's default behavior anyway
 -                 //   because we've removed Prompt and we're preserving whatever
 -                 //   it's rendered only.
 -                 return this._cloneElement(el, /* props */ {
 -                     onRequestClose: this._onCancel,
 -                     supportedOrientations: [ 'landscape', 'portrait' ]
 -                 });
 -             }
 - 
 -             if (type === TextInput) {
 -                 // * If this Dialog has children, they are to be rendered
 -                 //   instead of Prompt's TextInput.
 -                 if (children) {
 -                     // $FlowExpectedError
 -                     el = children; // eslint-disable-line no-param-reassign
 -                     children = undefined;
 -                 }
 - 
 -             } else {
 -                 let { style } = el.props;
 - 
 -                 if (style
 -                         && (style = StyleSheet.flatten(style))
 -                         && _TAG_KEY in style) {
 -                     // $FlowExpectedError
 -                     switch (style[_TAG_KEY]) {
 -                     case _SUBMIT_TEXT_TAG_VALUE:
 -                         if (this.state.submitting) {
 -                             // * If this Dialog is submitting, render a
 -                             //   LoadingIndicator.
 -                             return (
 -                                 <LoadingIndicator
 -                                     color = { submitButtonTextStyle.color }
 -                                     size = { 'small' } />
 -                             );
 -                         }
 -                         break;
 -                     }
 - 
 -                     return this._cloneElement(el, /* props */ {
 -                         style: set(style, _TAG_KEY, undefined)
 -                     });
 -                 }
 -             }
 - 
 -             return el;
 -         });
 - 
 -         return el;
 -     }
 - 
 -     /**
 -      * Clones a specific {@code ReactElement} and adds/merges specific props
 -      * into the clone.
 -      *
 -      * @param {ReactElement} el - The {@code ReactElement} to clone.
 -      * @param {Object} props - The props to add/merge into the clone.
 -      * @returns {ReactElement} The close of the specified {@code el} with
 -      * the specified {@code props} added/merged.
 -      */
 -     _cloneElement(el: React$Element<*>, props) {
 -         return (
 -             React.cloneElement(
 -                 el,
 -                 props,
 -                 ...React.Children.toArray(el.props.children)));
 -     }
 - 
 -     /**
 -      * Creates a deep clone of a specific {@code ReactElement} with the results
 -      * of calling a specific function on every node of a specific
 -      * {@code ReactElement} tree.
 -      *
 -      * @param {ReactElement} el - The {@code ReactElement} to clone and
 -      * call the specified {@code f} on.
 -      * @param {Function} f - The function to call on every node of the
 -      * {@code ReactElement} tree represented by the specified {@code el}.
 -      * @private
 -      * @returns {ReactElement}
 -      */
 -     _mapReactElement(
 -             el: ?React$Element<*>,
 -             f: (React$Element<*>) => ?React$Element<*>): ?React$Element<*> {
 -         if (!el || !el.props || !el.type) {
 -             return el;
 -         }
 - 
 -         let mapped = f(el);
 - 
 -         if (mapped) {
 -             const { children } = mapped.props;
 - 
 -             if (mapped === el || React.Children.count(children)) {
 -                 mapped
 -                     = React.cloneElement(
 -                         mapped,
 -                         /* props */ {},
 -                         ...React.Children.toArray(React.Children.map(
 -                             children,
 -                             function(el) { // eslint-disable-line no-shadow
 -                                 // eslint-disable-next-line no-invalid-this
 -                                 return this._mapReactElement(el, f);
 -                             },
 -                             this)));
 -             }
 -         }
 - 
 -         return mapped;
 -     }
 - 
 -     _onCancel: () => void;
 - 
 -     _onChangeText: (string) => void;
 - 
 -     /**
 -      * Notifies this {@code Dialog} that the text/value of the {@code TextInput}
 -      * rendered by {@code Prompt} has changed.
 -      *
 -      * @param {string} text - The new text/value of the {@code TextInput}
 -      * rendered by {@code Prompt}.
 -      * @returns {void}
 -      */
 -     _onChangeText(text: string) {
 -         this.setState({ text });
 -     }
 - 
 -     _onSubmit: (?string) => void;
 - 
 -     /**
 -      * Submits this {@code Dialog} with the value of the {@link TextInput}
 -      * rendered by {@link Prompt} unless a value is explicitly specified.
 -      *
 -      * @override
 -      * @param {string} [value] - The submitted value if any.
 -      * @returns {void}
 -      */
 -     _onSubmit(value: ?string) {
 -         // $FlowFixMeState
 -         super._onSubmit(value || this.state.text);
 -     }
 - }
 - 
 - export default translate(connect()(Dialog));
 
 
  |