123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- 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} from 'sentry/types/organization';
- import type {Relay} from 'sentry/types/relay';
- import createTrustedRelaysResponseError from './createTrustedRelaysResponseError';
- import Form from './form';
- import Modal from './modal';
- type FormProps = React.ComponentProps<typeof Form>;
- type Values = FormProps['values'];
- type Props = ModalRenderProps & {
- api: Client;
- onSubmitSuccess: (organization: Organization) => void;
- orgSlug: Organization['slug'];
- savedRelays: Array<Relay>;
- };
- type State = {
- disables: FormProps['disables'];
- errors: FormProps['errors'];
- isFormValid: boolean;
- requiredValues: Array<keyof Values>;
- title: string;
- values: Values;
- };
- class DialogManager<P extends Props = Props, S extends State = State> 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<S> {
- return {
- values: {name: '', publicKey: '', description: ''},
- requiredValues: ['name', 'publicKey'],
- errors: {},
- disables: {},
- isFormValid: false,
- title: this.getTitle(),
- } as Readonly<S>;
- }
- getTitle(): string {
- return '';
- }
- getData(): {trustedRelays: Array<Relay>} {
- // 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<F extends keyof Values>(field: F) {
- this.setState(prevState => ({
- errors: omit(prevState.errors, field),
- }));
- }
- handleErrorResponse(error: ReturnType<typeof createTrustedRelaysResponseError>) {
- 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 = <F extends keyof Values>(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 =
- <F extends keyof Values>(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 (
- <Form
- isFormValid={isFormValid}
- onSave={this.handleSave}
- onChange={this.handleChange}
- onValidate={this.handleValidate}
- onValidateKey={this.handleValidateKey}
- errors={errors}
- values={values}
- disables={disables}
- />
- );
- }
- getContent(): React.ReactElement {
- return this.getForm();
- }
- render() {
- const {title, isFormValid} = this.state;
- const btnSaveLabel = this.getBtnSaveLabel();
- const content = this.getContent();
- return (
- <Modal
- {...this.props}
- title={title}
- onSave={this.handleSave}
- btnSaveLabel={btnSaveLabel}
- disabled={!isFormValid}
- content={content}
- />
- );
- }
- }
- export default DialogManager;
|