import {Component} from 'react'; import isEqual from 'lodash/isEqual'; import omit from 'lodash/omit'; import {addErrorMessage} from 'sentry/actionCreators/indicator'; import type {ModalRenderProps} from 'sentry/actionCreators/modal'; import type {Client} from 'sentry/api'; import {t} from 'sentry/locale'; import type {Organization, Relay} from 'sentry/types'; import createTrustedRelaysResponseError from './createTrustedRelaysResponseError'; import Form from './form'; import Modal from './modal'; type FormProps = React.ComponentProps; type Values = FormProps['values']; type Props = ModalRenderProps & { api: Client; onSubmitSuccess: (organization: Organization) => void; orgSlug: Organization['slug']; savedRelays: Array; }; type State = { disables: FormProps['disables']; errors: FormProps['errors']; isFormValid: boolean; requiredValues: Array; title: string; values: Values; }; class DialogManager

extends Component< P, S > { state = this.getDefaultState(); componentDidMount() { this.validateForm(); } componentDidUpdate(_prevProps: Props, prevState: S) { if (!isEqual(prevState.values, this.state.values)) { this.validateForm(); } if ( !isEqual(prevState.errors, this.state.errors) && Object.keys(this.state.errors).length > 0 ) { this.setValidForm(false); } } getDefaultState(): Readonly { return { values: {name: '', publicKey: '', description: ''}, requiredValues: ['name', 'publicKey'], errors: {}, disables: {}, isFormValid: false, title: this.getTitle(), } as Readonly; } getTitle(): string { return ''; } getData(): {trustedRelays: Array} { // Child has to implement this throw new Error('Not implemented'); } getBtnSaveLabel(): string | undefined { return undefined; } setValidForm(isFormValid: boolean) { this.setState({isFormValid}); } validateForm() { const {values, requiredValues, errors} = this.state; const isFormValid = requiredValues.every( requiredValue => !!values[requiredValue].replace(/\s/g, '') && !errors[requiredValue] ); this.setValidForm(isFormValid); } clearError(field: F) { this.setState(prevState => ({ errors: omit(prevState.errors, field), })); } handleErrorResponse(error: ReturnType) { switch (error.type) { case 'invalid-key': case 'missing-key': this.setState(prevState => ({ errors: {...prevState.errors, publicKey: error.message}, })); break; case 'empty-name': case 'missing-name': this.setState(prevState => ({ errors: {...prevState.errors, name: error.message}, })); break; default: addErrorMessage(error.message); } } handleChange = (field: F, value: Values[F]) => { this.setState(prevState => ({ values: { ...prevState.values, [field]: value, }, errors: omit(prevState.errors, field), })); }; handleSave = async () => { const {onSubmitSuccess, closeModal, orgSlug, api} = this.props; const trustedRelays = this.getData().trustedRelays.map(trustedRelay => omit(trustedRelay, ['created', 'lastModified']) ); try { const response = await api.requestPromise(`/organizations/${orgSlug}/`, { method: 'PUT', data: {trustedRelays}, }); onSubmitSuccess(response); closeModal(); } catch (error) { this.handleErrorResponse(createTrustedRelaysResponseError(error)); } }; handleValidate = (field: F) => () => { const isFieldValueEmpty = !this.state.values[field].replace(/\s/g, ''); const fieldErrorAlreadyExist = this.state.errors[field]; if (isFieldValueEmpty && fieldErrorAlreadyExist) { return; } if (isFieldValueEmpty && !fieldErrorAlreadyExist) { this.setState(prevState => ({ errors: { ...prevState.errors, [field]: t('Field Required'), }, })); return; } if (!isFieldValueEmpty && fieldErrorAlreadyExist) { this.clearError(field); } }; handleValidateKey = () => { const {savedRelays} = this.props; const {values, errors} = this.state; const isKeyAlreadyTaken = savedRelays.find( savedRelay => savedRelay.publicKey === values.publicKey ); if (isKeyAlreadyTaken && !errors.publicKey) { this.setState({ errors: { ...errors, publicKey: t('Relay key already taken'), }, }); return; } if (errors.publicKey) { this.setState({ errors: omit(errors, 'publicKey'), }); } this.handleValidate('publicKey')(); }; getForm() { const {values, errors, disables, isFormValid} = this.state; return (

); } getContent(): React.ReactElement { return this.getForm(); } render() { const {title, isFormValid} = this.state; const btnSaveLabel = this.getBtnSaveLabel(); const content = this.getContent(); return ( ); } } export default DialogManager;