// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {Dimensions, ScrollView, View} from 'react-native'; import {Navigation} from 'react-native-navigation'; import {checkDialogElementForError, checkIfErrorsMatchElements} from '@mm-redux/utils/integration_utils'; import ErrorText from 'app/components/error_text'; import StatusBar from 'app/components/status_bar'; import FormattedText from 'app/components/formatted_text'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import {dismissModal} from 'app/actions/navigation'; import DialogElement from './dialog_element.js'; import DialogIntroductionText from './dialog_introduction_text.js'; export default class InteractiveDialog extends PureComponent { static propTypes = { url: PropTypes.string.isRequired, callbackId: PropTypes.string, introductionText: PropTypes.string, elements: PropTypes.arrayOf(PropTypes.object), notifyOnCancel: PropTypes.bool, state: PropTypes.string, theme: PropTypes.object, actions: PropTypes.shape({ submitInteractiveDialog: PropTypes.func.isRequired, }).isRequired, }; static defaultProps = { url: '', elements: [], }; constructor(props) { super(props); const values = {}; if (props.elements != null) { props.elements.forEach((e) => { if (e.type === 'bool') { values[e.name] = (e.default === true || String(e.default).toLowerCase() === 'true'); } else { values[e.name] = e.default || null; } }); } this.state = { values, error: null, errors: {}, isLandscape: this.isLandscape(), submitting: false, }; this.scrollView = React.createRef(); } componentDidMount() { this.navigationEventListener = Navigation.events().bindComponent(this); Dimensions.addEventListener('change', this.orientationDidChange); } componentWillUnmount() { Dimensions.removeEventListener('change', this.orientationDidChange); } orientationDidChange = () => { this.setState({isLandscape: this.isLandscape()}); } isLandscape = () => { const {height, width} = Dimensions.get('window'); return width > height; } navigationButtonPressed({buttonId}) { switch (buttonId) { case 'submit-dialog': this.handleSubmit(); break; case 'close-dialog': this.notifyOnCancelIfNeeded(); this.handleHide(); break; } } handleSubmit = async () => { const {elements} = this.props; const values = this.state.values; const errors = {}; if (elements) { elements.forEach((elem) => { const error = checkDialogElementForError(elem, values[elem.name]); if (error) { errors[elem.name] = ( ); } }); } this.setState({errors}); if (Object.keys(errors).length !== 0) { return; } const {url, callbackId, state} = this.props; const dialog = { url, callback_id: callbackId, state, submission: values, }; const {data} = await this.props.actions.submitInteractiveDialog(dialog); this.submitted = true; let hasErrors = false; if (data) { if (data.errors && Object.keys(data.errors).length >= 0 && checkIfErrorsMatchElements(data.errors, elements) ) { hasErrors = true; this.setState({errors: data.errors}); } if (data.error) { hasErrors = true; this.setState({error: data.error}); if (this.scrollView?.current) { this.scrollView.current.scrollTo({x: 0, y: 0}); } } } if (!hasErrors) { this.handleHide(); } } notifyOnCancelIfNeeded = () => { if (this.submitted) { return; } const {url, callbackId, state, notifyOnCancel} = this.props; if (!notifyOnCancel) { return; } const dialog = { url, callback_id: callbackId, state, cancelled: true, }; this.props.actions.submitInteractiveDialog(dialog); } handleHide = () => { dismissModal(); } onChange = (name, value) => { const values = {...this.state.values, [name]: value}; this.setState({values}); } render() { const {introductionText, elements, theme} = this.props; const {error, errors, isLandscape, values} = this.state; const style = getStyleFromTheme(theme); return ( {error && ( )} {Boolean(introductionText) && } {elements && elements.map((e) => { return ( ); })} ); } } const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { return { container: { backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), }, errorContainer: { marginTop: 15, marginLeft: 15, fontSize: 14, fontWeight: 'bold', }, scrollView: { marginBottom: 20, marginTop: 10, }, }; });