AugurProject/augur-ui

View on GitHub
src/modules/modal/components/modal-network-connect.jsx

Summary

Maintainability
C
1 day
Test Coverage
import React, { Component } from "react";
import PropTypes from "prop-types";
import classNames from "classnames";

import { ExclamationCircle as InputErrorIcon } from "modules/common/components/icons";
import Input from "modules/common/components/input/input";

import Styles from "modules/modal/components/common/common.styles";
import ModalActions from "modules/modal/components/common/modal-actions";
import { windowRef } from "src/utils/window-ref";
import { editEndpointParams } from "src/utils/edit-endpoint-params";
import { MODAL_NETWORK_CONNECT } from "modules/modal/constants/modal-types";

export default class ModalNetworkConnect extends Component {
  static propTypes = {
    modal: PropTypes.shape({
      isInitialConnection: PropTypes.bool
    }).isRequired,
    env: PropTypes.object.isRequired,
    submitForm: PropTypes.func.isRequired,
    isConnectedThroughWeb3: PropTypes.bool.isRequired
  };

  constructor(props) {
    super(props);
    // prioritize ethereumNode connections
    let ethereumNode = "";
    if (props.env["ethereum-node"]) {
      if (props.env["ethereum-node"].ipc) {
        ethereumNode = props.env["ethereum-node"].ipc;
      } else if (props.env["ethereum-node"].ws) {
        ethereumNode = props.env["ethereum-node"].ws;
      } else if (props.env["ethereum-node"].http) {
        ethereumNode = props.env["ethereum-node"].http;
      }
    }

    this.state = {
      augurNode: props.env["augur-node"] || "",
      ethereumNode,
      connectErrors: [],
      formErrors: {
        augurNode: [],
        ethereumNode: []
      }
    };

    this.types = { IPC: "ipc", HTTP: "http", WS: "ws" };

    this.submitForm = this.submitForm.bind(this);
    this.validateField = this.validateField.bind(this);
    this.isFormInvalid = this.isFormInvalid.bind(this);
    this.calcProtocol = this.calcProtocol.bind(this);
  }

  calcProtocol(uri) {
    const { types } = this;
    if (typeof uri === "string" && uri.length && uri.includes("://")) {
      if (uri.includes(types.IPC)) return types.IPC;
      if (uri.includes(types.WS)) return types.WS;
      if (uri.includes(types.HTTP)) return types.HTTP;
    }
    return false;
  }

  validateField(field, value) {
    const { formErrors } = this.state;
    const connectErrors = [];
    formErrors[field] = [];

    if (!value || value.length === 0)
      formErrors[field].push(`This field is required.`);

    this.setState({ connectErrors, formErrors, [field]: value });
  }

  isFormInvalid() {
    const { isConnectedThroughWeb3 } = this.props;
    const { augurNode, ethereumNode } = this.state;
    return !(
      augurNode.length &&
      (ethereumNode.length || isConnectedThroughWeb3)
    );
  }

  submitForm(e) {
    const { submitForm, env } = this.props;
    const { ethereumNode, augurNode } = this.state;
    let ethNode = {};
    const protocol = this.calcProtocol(ethereumNode);
    if (protocol) {
      ethNode = {
        [`${protocol}`]: ethereumNode
      };
    }

    // because we prioritize, lets wipe out all previous connection options but not remove things like timeout.
    const updatedEnv = {
      ...env,
      "augur-node": augurNode,
      "ethereum-node": {
        ...env["ethereum-node"],
        ipc: "",
        http: "",
        ws: "",
        ...ethNode
      }
    };
    const endpoints = {
      augurNode: updatedEnv["augur-node"],
      ethereumNodeHTTP: updatedEnv["ethereum-node"].http,
      ethereumNodeWS: updatedEnv["ethereum-node"].ws
    };

    // reloads window
    editEndpointParams(windowRef, endpoints);

    // this.props.submitForm used as a hook for disconnection modal, normally just preventsDefault
    submitForm(e);
  }

  render() {
    const { isConnectedThroughWeb3, modal } = this.props;
    const { formErrors, connectErrors, augurNode, ethereumNode } = this.state;
    const AugurNodeInValid = formErrors.augurNode.length > 0;
    const ethereumNodeInValid = formErrors.ethereumNode.length > 0;
    const hasConnectionErrors = connectErrors.length > 0;
    const formInvalid = this.isFormInvalid();

    return (
      <form
        className={classNames(Styles.ModalForm, {
          [`${Styles.ModalContainer}`]: modal.type === MODAL_NETWORK_CONNECT
        })}
      >
        <h1>Connect to Augur</h1>
        <label htmlFor="modal__augurNode-input">Augur Node Address:</label>
        <Input
          id="modal__augurNode-input"
          type="text"
          className={classNames({
            [`${Styles.ErrorField}`]: AugurNodeInValid
          })}
          value={augurNode}
          placeholder="Enter the augurNode address you would like to connect to."
          onChange={value => this.validateField("augurNode", value)}
          required
        />
        {AugurNodeInValid &&
          formErrors.augurNode.map(error => (
            <p key={error} className={Styles.Error}>
              {InputErrorIcon} {error}
            </p>
          ))}
        <label htmlFor="modal__ethNode-input">Ethereum Node address:</label>
        {isConnectedThroughWeb3 && (
          <div>
            You are already connected to an Ethereum Node through Metamask. If
            you would like to specify a node, please disable Metamask.
          </div>
        )}
        {!isConnectedThroughWeb3 && (
          <Input
            id="modal__ethNode-input"
            type="text"
            className={classNames({
              [`${Styles.ErrorField}`]: ethereumNodeInValid
            })}
            value={ethereumNode}
            placeholder="Enter the Ethereum Node address you would like to connect to."
            onChange={value => this.validateField("ethereumNode", value)}
            required
          />
        )}
        {!isConnectedThroughWeb3 &&
          ethereumNodeInValid &&
          formErrors.ethereumNode.map(error => (
            <p key={error} className={Styles.Error}>
              {InputErrorIcon} {error}
            </p>
          ))}
        {hasConnectionErrors &&
          connectErrors.map(error => (
            <p key={error} className={Styles.Error}>
              {InputErrorIcon} {error}
            </p>
          ))}
        <ModalActions
          buttons={[
            {
              label: "Connect",
              isDisabled: formInvalid,
              type: "purple",
              action: this.submitForm
            }
          ]}
        />
      </form>
    );
  }
}