mirror of https://github.com/jitsi/jitsi-meet
parent
3212bde6e6
commit
80e8afa9c1
@ -1,299 +0,0 @@ |
||||
// @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, |
||||
|
||||
/** |
||||
* Function to be used to retreive translated i18n labels. |
||||
*/ |
||||
t: Function, |
||||
|
||||
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)); |
Loading…
Reference in new issue