diff --git a/react/features/base/dialog/components/AbstractDialog.js b/react/features/base/dialog/components/AbstractDialog.js
index 1549e17ec..d9633f51f 100644
--- a/react/features/base/dialog/components/AbstractDialog.js
+++ b/react/features/base/dialog/components/AbstractDialog.js
@@ -1,32 +1,38 @@
-import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Component } from 'react';
import { hideDialog } from '../actions';
import { DIALOG_PROP_TYPES } from '../constants';
/**
- * Abstract dialog to display dialogs.
+ * An abstract implementation of a dialog on Web/React and mobile/react-native.
*/
export default class AbstractDialog extends Component {
-
/**
- * Abstract Dialog component's property types.
+ * AbstractDialog React Component's prop types.
*
* @static
*/
static propTypes = {
...DIALOG_PROP_TYPES,
+ /**
+ * The React Component children of AbstractDialog
+ * which represents the dialog's body.
+ */
+ children: PropTypes.node,
+
/**
* Used to show/hide the dialog on cancel.
*/
- dispatch: React.PropTypes.func
+ dispatch: PropTypes.func
};
/**
- * Initializes a new Dialog instance.
+ * Initializes a new AbstractDialog instance.
*
- * @param {Object} props - The read-only properties with which the new
- * instance is to be initialized.
+ * @param {Object} props - The read-only React Component props with
+ * which the new instance is to be initialized.
*/
constructor(props) {
super(props);
@@ -36,37 +42,30 @@ export default class AbstractDialog extends Component {
}
/**
- * Dispatches action to hide the dialog.
+ * Dispatches a redux action to hide this dialog when it's canceled.
*
+ * @protected
* @returns {void}
*/
_onCancel() {
- let hide = true;
+ const { onCancel } = this.props;
- if (this.props.onCancel) {
- hide = this.props.onCancel();
- }
-
- if (hide) {
+ if (!onCancel || onCancel()) {
this.props.dispatch(hideDialog());
}
}
/**
- * Dispatches the action when submitting the dialog.
+ * Dispatches a redux action to hide this dialog when it's submitted.
*
* @private
* @param {string} value - The submitted value if any.
* @returns {void}
*/
_onSubmit(value) {
- let hide = true;
+ const { onSubmit } = this.props;
- if (this.props.onSubmit) {
- hide = this.props.onSubmit(value);
- }
-
- if (hide) {
+ if (!onSubmit || onSubmit(value)) {
this.props.dispatch(hideDialog());
}
}
diff --git a/react/features/base/dialog/components/Dialog.native.js b/react/features/base/dialog/components/Dialog.native.js
index e2637fc96..fbc102562 100644
--- a/react/features/base/dialog/components/Dialog.native.js
+++ b/react/features/base/dialog/components/Dialog.native.js
@@ -1,4 +1,6 @@
+import PropTypes from 'prop-types';
import React from 'react';
+import { TextInput } from 'react-native';
import Prompt from 'react-native-prompt';
import { connect } from 'react-redux';
@@ -7,20 +9,21 @@ import { translate } from '../../i18n';
import AbstractDialog from './AbstractDialog';
/**
- * Native dialog using Prompt.
+ * Implements AbstractDialog on react-native using Prompt.
*/
class Dialog extends AbstractDialog {
-
/**
- * Native sialog component's property types.
+ * AbstractDialog's React Component prop types.
*
* @static
*/
static propTypes = {
+ ...AbstractDialog.propTypes,
+
/**
* I18n key to put as body title.
*/
- bodyKey: React.PropTypes.string
+ bodyKey: PropTypes.string
};
/**
@@ -31,27 +34,109 @@ class Dialog extends AbstractDialog {
*/
render() {
const {
- cancelDisabled,
- cancelTitleKey,
bodyKey,
+ cancelDisabled,
+ cancelTitleKey = 'dialog.Cancel',
+ children,
okDisabled,
- okTitleKey,
+ okTitleKey = 'dialog.Ok',
t,
- titleKey
+ titleKey,
+ titleString
} = this.props;
- return (
-
- );
+ submitText = { okDisabled ? undefined : t(okTitleKey) }
+ title = { titleString || t(titleKey) }
+ visible = { true } />;
+
+ /* eslint-enable react/jsx-wrap-multilines */
+
+ if (React.Children.count(children)) {
+ // XXX The following implements a workaround with knowledge of the
+ // implementation of react-native-prompt.
+ element
+ = this._replaceFirstElementOfType(
+ // eslint-disable-next-line no-extra-parens, new-cap
+ (new (element.type)(element.props)).render(),
+ TextInput,
+ children);
+ }
+
+ return element;
+ }
+
+ /**
+ * Creates a deep clone of a specific ReactElement with the results
+ * of calling a specific function on every node of a specific
+ * ReactElement tree.
+ *
+ * @param {ReactElement} element - The ReactElement to clone and
+ * call the specified f on.
+ * @param {Function} f - The function to call on every node of the
+ * ReactElement tree represented by the specified element.
+ * @private
+ * @returns {ReactElement}
+ */
+ _mapReactElement(element, f) {
+ if (!element || !element.props || !element.type) {
+ return element;
+ }
+
+ let mapped = f(element);
+
+ if (mapped === element) {
+ mapped
+ = React.cloneElement(
+ element,
+ /* props */ undefined,
+ ...React.Children.toArray(React.Children.map(
+ element.props.children,
+ function(element) { // eslint-disable-line no-shadow
+ // eslint-disable-next-line no-invalid-this
+ return this._mapReactElement(element, f);
+ },
+ this)));
+ }
+
+ return mapped;
+ }
+
+ /**
+ * Replaces the first ReactElement of a specific type found in a
+ * specific ReactElement tree with a specific replacement
+ * ReactElement.
+ *
+ * @param {ReactElement} element - The ReactElement tree to search
+ * through and replace in.
+ * @param {*} type - The type of the ReactElement to be replaced.
+ * @param {ReactElement} replacement - The ReactElement to replace
+ * the first ReactElement in element of the specified
+ * type.
+ * @private
+ * @returns {ReactElement}
+ */
+ _replaceFirstElementOfType(element, type, replacement) {
+ // eslint-disable-next-line no-shadow
+ return this._mapReactElement(element, element => {
+ if (replacement && element.type === type) {
+ /* eslint-disable no-param-reassign */
+
+ element = replacement;
+ replacement = undefined;
+
+ /* eslint-enable no-param-reassign */
+ }
+
+ return element;
+ });
}
}
diff --git a/react/features/base/dialog/components/Dialog.web.js b/react/features/base/dialog/components/Dialog.web.js
index 006fc345d..f7790e4e0 100644
--- a/react/features/base/dialog/components/Dialog.web.js
+++ b/react/features/base/dialog/components/Dialog.web.js
@@ -1,3 +1,4 @@
+import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
@@ -8,7 +9,6 @@ import StatelessDialog from './StatelessDialog';
* Web dialog that uses atlaskit modal-dialog to display dialogs.
*/
class Dialog extends AbstractDialog {
-
/**
* Web dialog component's property types.
*
@@ -17,21 +17,16 @@ class Dialog extends AbstractDialog {
static propTypes = {
...AbstractDialog.propTypes,
- /**
- * This is the body of the dialog, the component children.
- */
- children: React.PropTypes.node,
-
/**
* Whether the dialog is modal. This means clicking on the blanket will
* leave the dialog open. No cancel button.
*/
- isModal: React.PropTypes.bool,
+ isModal: PropTypes.bool,
/**
* Disables rendering of the submit button.
*/
- submitDisabled: React.PropTypes.bool,
+ submitDisabled: PropTypes.bool,
/**
* Width of the dialog, can be:
@@ -40,7 +35,7 @@ class Dialog extends AbstractDialog {
* - integer value for pixel width
* - string value for percentage
*/
- width: React.PropTypes.string
+ width: PropTypes.string
};
/**
@@ -65,8 +60,8 @@ class Dialog extends AbstractDialog {
render() {
const props = {
...this.props,
- onSubmit: this._onSubmit,
- onCancel: this._onCancel
+ onCancel: this._onCancel,
+ onSubmit: this._onSubmit
};
delete props.dispatch;
@@ -80,11 +75,7 @@ class Dialog extends AbstractDialog {
* @returns {void}
*/
_onCancel() {
- if (this.props.isModal) {
- return;
- }
-
- super._onCancel();
+ this.props.isModal || super._onCancel();
}
}
diff --git a/react/features/base/dialog/constants.js b/react/features/base/dialog/constants.js
index ec112b730..676add1d0 100644
--- a/react/features/base/dialog/constants.js
+++ b/react/features/base/dialog/constants.js
@@ -1,50 +1,50 @@
-import React from 'react';
+import PropTypes from 'prop-types';
export const DIALOG_PROP_TYPES = {
/**
* Whether cancel button is disabled. Enabled by default.
*/
- cancelDisabled: React.PropTypes.bool,
+ cancelDisabled: PropTypes.bool,
/**
* Optional i18n key to change the cancel button title.
*/
- cancelTitleKey: React.PropTypes.string,
+ cancelTitleKey: PropTypes.string,
/**
* Is ok button enabled/disabled. Enabled by default.
*/
- okDisabled: React.PropTypes.bool,
+ okDisabled: PropTypes.bool,
/**
* Optional i18n key to change the ok button title.
*/
- okTitleKey: React.PropTypes.string,
+ okTitleKey: PropTypes.string,
/**
* The handler for onCancel event.
*/
- onCancel: React.PropTypes.func,
+ onCancel: PropTypes.func,
/**
* The handler for the event when submitting the dialog.
*/
- onSubmit: React.PropTypes.func,
+ onSubmit: PropTypes.func,
/**
* Used to obtain translations in children classes.
*/
- t: React.PropTypes.func,
+ t: PropTypes.func,
/**
* Key to use for showing a title.
*/
- titleKey: React.PropTypes.string,
+ titleKey: PropTypes.string,
/**
* The string to use as a title instead of {@code titleKey}. If a truthy
* value is specified, it takes precedence over {@code titleKey} i.e.
* the latter is unused.
*/
- titleString: React.PropTypes.string
+ titleString: PropTypes.string
};