react-bootstrap#FormControl TypeScript Examples

The following examples show how to use react-bootstrap#FormControl. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: CustomMenu.tsx    From 3Speak-app with GNU General Public License v3.0 6 votes vote down vote up
CustomPinsViewMenu = React.forwardRef(
  ({ children, style, className, 'aria-labelledby': labeledBy }: any, ref: any) => {
    const [value, setValue] = useState('')

    return (
      <div ref={ref} style={style} className={className} aria-labelledby={labeledBy}>
        <FormControl
          autoFocus
          className="mx-3 my-2 w-auto"
          placeholder="Type to filter..."
          onChange={(e) => setValue(e.target.value)}
          value={value}
        />
        <ul className="list-unstyled">
          {React.Children.toArray(children).filter(
            (child: any) => !value || child.props.children.toLowerCase().startsWith(value),
          )}
        </ul>
      </div>
    )
  },
)
Example #2
Source File: MysticCodePicker.tsx    From apps with MIT License 6 votes vote down vote up
render() {
        return (
            <div>
                Jump to:
                <FormControl
                    as={"select"}
                    custom
                    onChange={(ev: Event) => {
                        this.changeMysticCode(parseInt(ev.target.value));
                    }}
                    value={this.props.id}
                >
                    {this.props.mysticCodes.map((mysticCode) => {
                        return (
                            <option key={mysticCode.id} value={mysticCode.id}>
                                {mysticCode.name}
                            </option>
                        );
                    })}
                </FormControl>
            </div>
        );
    }
Example #3
Source File: CommandCodePicker.tsx    From apps with MIT License 6 votes vote down vote up
render() {
        return (
            <div>
                Jump to:
                <FormControl
                    as={"select"}
                    custom
                    onChange={(ev: Event) => {
                        this.changeCommandCode(parseInt(ev.target.value));
                    }}
                    value={this.props.id}
                >
                    {this.props.commandCodes
                        .slice()
                        .reverse()
                        .map((commandCode) => {
                            return (
                                <option key={commandCode.id} value={commandCode.id}>
                                    {commandCode.name}
                                </option>
                            );
                        })}
                </FormControl>
            </div>
        );
    }
Example #4
Source File: Login.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 6 votes vote down vote up
onEmailChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement;
    this.setState({
      email: target.value,
      emailValid: emailRegex.test(target.value.toLowerCase())
        ? "success"
        : "error",
    });
  };
Example #5
Source File: Signup.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 6 votes vote down vote up
onEmailChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement;
    this.setState({
      email: target.value,
      emailValid: emailRegex.test(target.value.toLowerCase())
        ? "success"
        : "error",
    });
  };
Example #6
Source File: Signup.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
onConfirmationCodeChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement;
    this.setState({
      confirmationCode: target.value,
      confirmationCodeValid: target.value.length > 0 ? "error" : "success",
    });
  };
Example #7
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 5 votes vote down vote up
ProposalEditor = ({
  title,
  body,
  onTitleInput,
  onBodyInput,
}: {
  title: string;
  body: string;
  onTitleInput: (title: string) => void;
  onBodyInput: (body: string) => void;
}) => {
  const bodyPlaceholder = `## Summary\n\nInsert your summary here\n\n## Methodology\n\nInsert your methodology here\n\n## Conclusion\n\nInsert your conclusion here`;
  const [proposalText, setProposalText] = useState('');

  const onBodyChange = (body: string) => {
    setProposalText(body);
    onBodyInput(body);
  };

  return (
    <div>
      <InputGroup className={`${classes.proposalEditor} d-flex flex-column`}>
        <FormText>
          <Trans>Proposal</Trans>
        </FormText>
        <FormControl
          className={classes.titleInput}
          value={title}
          onChange={e => onTitleInput(e.target.value)}
          placeholder="Proposal Title"
        />
        <hr className={classes.divider} />
        <FormControl
          className={classes.bodyInput}
          value={body}
          onChange={e => onBodyChange(e.target.value)}
          as="textarea"
          placeholder={bodyPlaceholder}
        />
      </InputGroup>
      {proposalText !== '' && (
        <div className={classes.previewArea}>
          <h3>
            <Trans>Preview:</Trans>
          </h3>
          <ReactMarkdown
            className={classes.markdown}
            children={proposalText}
            remarkPlugins={[remarkBreaks]}
          />
        </div>
      )}
    </div>
  );
}
Example #8
Source File: PhotoSwipe.tsx    From bada-frame with GNU General Public License v3.0 5 votes vote down vote up
FileNameEditForm = ({ filename, saveEdits, discardEdits, extension }) => {
    const [loading, setLoading] = useState(false);

    const onSubmit = async (values: formValues) => {
        try {
            setLoading(true);
            await saveEdits(values.filename);
        } finally {
            setLoading(false);
        }
    };
    return (
        <Formik<formValues>
            initialValues={{ filename }}
            validationSchema={Yup.object().shape({
                filename: Yup.string()
                    .required(constants.REQUIRED)
                    .max(
                        MAX_EDITED_FILE_NAME_LENGTH,
                        constants.FILE_NAME_CHARACTER_LIMIT
                    ),
            })}
            validateOnBlur={false}
            onSubmit={onSubmit}>
            {({ values, errors, handleChange, handleSubmit }) => (
                <Form noValidate onSubmit={handleSubmit}>
                    <Form.Row>
                        <Form.Group
                            bsPrefix="ente-form-group"
                            as={Col}
                            xs={extension ? 7 : 8}>
                            <Form.Control
                                as="textarea"
                                placeholder={constants.FILE_NAME}
                                value={values.filename}
                                onChange={handleChange('filename')}
                                isInvalid={Boolean(errors.filename)}
                                autoFocus
                                disabled={loading}
                            />
                            <FormControl.Feedback
                                type="invalid"
                                style={{ textAlign: 'center' }}>
                                {errors.filename}
                            </FormControl.Feedback>
                        </Form.Group>
                        {extension && (
                            <Form.Group
                                bsPrefix="ente-form-group"
                                as={Col}
                                xs={1}
                                controlId="formHorizontalFileName">
                                <FlexWrapper style={{ padding: '5px' }}>
                                    {`.${extension}`}
                                </FlexWrapper>
                            </Form.Group>
                        )}
                        <Form.Group bsPrefix="ente-form-group" as={Col} xs={2}>
                            <Value width={'16.67%'}>
                                <IconButton type="submit" disabled={loading}>
                                    {loading ? (
                                        <SmallLoadingSpinner />
                                    ) : (
                                        <TickIcon />
                                    )}
                                </IconButton>
                                <IconButton
                                    onClick={discardEdits}
                                    disabled={loading}>
                                    <CloseIcon />
                                </IconButton>
                            </Value>
                        </Form.Group>
                    </Form.Row>
                </Form>
            )}
        </Formik>
    );
}
Example #9
Source File: Signup.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
showSignupForm = () => {
    return (
      <form onSubmit={this.onSignup}>
        <FormGroup controlId="email" validationState={this.state.emailValid}>
          <ControlLabel>Email</ControlLabel>
          <FormControl
            name="email"
            type="email"
            bsSize="large"
            value={this.state.email}
            onChange={this.onEmailChange}
          />
          <FormControl.Feedback />
        </FormGroup>
        <FormGroup
          controlId="password"
          validationState={this.state.passwordValid}
        >
          <ControlLabel>Password</ControlLabel>
          <FormControl
            name="password"
            type="password"
            bsSize="large"
            value={this.state.password}
            onChange={this.onPasswordChange}
          />
          <FormControl.Feedback />
          <HelpBlock>Must be at least 8 characters</HelpBlock>
        </FormGroup>
        <FormGroup
          controlId="confirmPassword"
          validationState={this.state.confirmPasswordValid}
        >
          <ControlLabel>Confirm Password</ControlLabel>
          <FormControl
            name="confirmPassword"
            type="password"
            bsSize="large"
            value={this.state.confirmPassword}
            onChange={this.onConfirmPasswordChange}
          />
          <FormControl.Feedback />
        </FormGroup>
        <Button
          block
          bsSize="large"
          type="submit"
          disabled={
            this.state.passwordValid !== "success" ||
            this.state.confirmPasswordValid !== "success" ||
            this.state.emailValid !== "success"
          }
        >
          {this.state.loading && (
            <Glyphicon glyph="refresh" className="spinning" />
          )}
          Log in
        </Button>
      </form>
    );
  };
Example #10
Source File: Signup.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
onConfirmPasswordChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement;
    this.setState({
      confirmPassword: target.value,
      confirmPasswordValid:
        target.value !== this.state.password ? "error" : "success",
    });
  };
Example #11
Source File: Signup.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
onPasswordChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement;
    this.setState({
      password: target.value,
      passwordValid: target.value.length < 8 ? "error" : "success",
    });
  };
Example #12
Source File: Login.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
onPasswordChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement;
    this.setState({
      password: target.value,
      passwordValid: target.value.length < 8 ? "error" : "success",
    });
  };
Example #13
Source File: Login.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
render() {
    if (this.state.redirect) return <Redirect to="/" />;

    return (
      <div className="Login">
        <form onSubmit={this.onLogin}>
          <FormGroup controlId="email" validationState={this.state.emailValid}>
            <ControlLabel>Email</ControlLabel>
            <FormControl
              name="email"
              type="email"
              bsSize="large"
              value={this.state.email}
              onChange={this.onEmailChange}
            />
            <FormControl.Feedback />
          </FormGroup>
          <FormGroup
            controlId="password"
            validationState={this.state.passwordValid}
          >
            <ControlLabel>Password</ControlLabel>
            <FormControl
              name="password"
              type="password"
              bsSize="large"
              value={this.state.password}
              onChange={this.onPasswordChange}
            />
            <FormControl.Feedback />
          </FormGroup>
          <Button
            block
            bsSize="large"
            type="submit"
            disabled={
              this.state.passwordValid !== "success" ||
              this.state.emailValid !== "success"
            }
          >
            {this.state.loading && (
              <Glyphicon glyph="refresh" className="spinning" />
            )}
            Log in
          </Button>
        </form>
      </div>
    );
  }
Example #14
Source File: CheckoutForm.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
handleChange = (event: React.FormEvent<FormControl>) => {
    const target = event.target as HTMLInputElement
    this.setState({ 
      ...this.state,
      [target.name as any]: target.value
    });
  }
Example #15
Source File: CheckoutForm.tsx    From tutorial-cloudflare-bookstore with Apache License 2.0 5 votes vote down vote up
render() {
    if (this.state.toConfirm) return <Redirect to='/checkout-confirm' />

    if (this.state.isLoading) return null;
    return (
      <div className="well-bs col-md-12 full-page no-padding-top">
        <div className="white-box no-margin-top">
          <div className="checkout ">
            <img src={supportedCards} alt="Supported cards" />
            <Form>
              <FormGroup
                controlId="card"
                validationState={this.getCardNumberValidationState()}>
                <ControlLabel>Card number</ControlLabel>
                <FormControl
                  name="card"
                  type="text"
                  value={this.state.card}
                  onChange={this.handleChange} />
                <FormControl.Feedback />
              </FormGroup>
              <div className="form-row">
                <FormGroup
                  controlId="expDate">
                  <ControlLabel>Expiration date</ControlLabel>
                  <FormControl
                    name="expDate"
                    type="date"
                    value={this.state.expDate}
                    onChange={this.handleChange} />
                  <FormControl.Feedback />
                </FormGroup>
                <FormGroup
                  className="ccv"
                  controlId="ccv">
                  <ControlLabel>CCV</ControlLabel>
                  <FormControl
                    name="ccv"
                    type="text"
                    value={this.state.ccv}
                    onChange={this.handleChange} />
                  <FormControl.Feedback />
                </FormGroup>
              </div>
            </Form>
          </div>
        </div>
        <div className="pull-right">
          <button className="btn btn-black" type="button" onClick={this.onCheckout}>{`Pay ($${this.getOrderTotal()})`}</button>
        </div>
      </div>
    );
  }
Example #16
Source File: TopNavbar.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function TopNavbar() {
  const [inEdit, setInEdit] = useState(false)
  const urlForm = useRef<any>()
  const [urlSplit, setUrlSplit] = useState([])

  const startEdit = () => {
    setInEdit(true)
  }

  useEffect(() => {
    if (inEdit) {
      urlForm.current?.focus()
    }
  }, [inEdit])

  const exitEdit = () => {
    setInEdit(false)
  }

  const finishEdit = (e) => {
    if (e.keyCode === 13) {
      if (location.hash !== `#${e.target.value}`) {
        location.replace(`#${e.target.value}`)
        location.reload()
      }
      setInEdit(false)
    } else if (e.keyCode === 27) {
      exitEdit()
    }
  }

  const updateUrlSplit = () => {
    const hash = window.location.hash
    const theUrlSplit = hash.split('/')
    theUrlSplit.splice(0, 1)

    if (theUrlSplit[0] === 'watch') {
      const pagePerm = theUrlSplit[1]
      const pagePermSpliced = pagePerm.split(':')
      pagePermSpliced.splice(0, 1)
      theUrlSplit.pop()
      pagePermSpliced.forEach((onePagePerm) => {
        theUrlSplit.push(onePagePerm)
      })

      setUrlSplit(theUrlSplit)
    } else {
      setUrlSplit(theUrlSplit)
    }
  }

  useEffect(() => {
    updateUrlSplit()
  }, [])

  useEffect(() => {
    window.addEventListener('hashchange', function (event) {
      updateUrlSplit()
    })
  }, [])

  const userProfileUrl = useMemo(() => {
    const windowLocationHash = window.location.hash
    const windowLocationSearch = windowLocationHash.search('#')
    const windowLocationHref = windowLocationHash.slice(windowLocationSearch)
    const hrefSegments = windowLocationHref.split('/')
    hrefSegments.splice(0, 1)

    let userProfileUrl = '#/user/'

    if (hrefSegments[0] === 'watch') {
      const userProfileUrlInit = hrefSegments[1]
      const userProfileUrlSpliced = userProfileUrlInit.split(':')
      userProfileUrlSpliced.pop()

      userProfileUrlSpliced.forEach((one) => {
        if (one === userProfileUrlSpliced[0]) {
          userProfileUrl = userProfileUrl + one + ':'
        } else {
          userProfileUrl = userProfileUrl + one
        }
      })
    }

    return userProfileUrl
  }, [])

  return (
    <div>
      <Navbar bg="light" expand="lg">
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav className="mr-auto">
            {!inEdit ? (
              <>
                <Breadcrumb>
                  <Breadcrumb.Item href="#/">Home</Breadcrumb.Item>
                  {urlSplit.map((el) =>
                    el === updateUrlSplit[1] && updateUrlSplit[0] === 'watch' ? (
                      <Breadcrumb.Item href={userProfileUrl} key={el} id={el}>
                        {el}
                      </Breadcrumb.Item>
                    ) : (
                      <Breadcrumb.Item href={'#'} key={el} id={el}>
                        {el}
                      </Breadcrumb.Item>
                    ),
                  )}
                </Breadcrumb>
                <Button
                  className="btn btn-light btn-sm"
                  style={{
                    marginLeft: '5px',
                    width: '40px',
                    height: '40px',
                    padding: '3.5%',
                    verticalAlign: 'baseline',
                  }}
                  onClick={startEdit}
                >
                  <FaEdit style={{ textAlign: 'center', verticalAlign: 'initial' }} />
                </Button>
              </>
            ) : (
              <FormControl
                ref={urlForm}
                defaultValue={(() => {
                  return location.hash.slice(1)
                })()}
                onKeyDown={finishEdit}
                onBlur={exitEdit}
              />
            )}
          </Nav>
          <Dropdown>
            <Dropdown.Toggle variant="secondary" size="lg">
              Options
            </Dropdown.Toggle>

            <Dropdown.Menu>
              <Dropdown.Item onClick={() => copyToClip(window.location.hash)}>
                Copy Current URL{' '}
                <FaCopy size={28} onClick={() => copyToClip(window.location.hash)} />
              </Dropdown.Item>
              <Dropdown.Item onClick={goToClip}>
                Go to Copied URL <FaArrowRight size={28} />
              </Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
          <Nav>
            <Nav.Link>
              <FaAngleLeft size={28} onClick={goBack} />
            </Nav.Link>
            <Nav.Link>
              <FaAngleRight size={28} onClick={goForth} />
            </Nav.Link>
          </Nav>
        </Navbar.Collapse>
      </Navbar>
    </div>
  )
}
Example #17
Source File: ChangeEmail.tsx    From bada-frame with GNU General Public License v3.0 4 votes vote down vote up
function ChangeEmailForm(props: Props) {
    const [loading, setLoading] = useState(false);
    const [ottInputVisible, setShowOttInputVisibility] = useState(false);
    const emailInputElement = useRef(null);
    const ottInputRef = useRef(null);
    const appContext = useContext(AppContext);

    useEffect(() => {
        setTimeout(() => {
            emailInputElement.current?.focus();
        }, 250);
    }, []);

    useEffect(() => {
        if (!ottInputVisible) {
            props.showMessage(false);
        }
    }, [ottInputVisible]);

    const requestOTT = async (
        { email }: formValues,
        { setFieldError }: FormikHelpers<formValues>
    ) => {
        try {
            setLoading(true);
            await getOTTForEmailChange(email);
            props.setEmail(email);
            setShowOttInputVisibility(true);
            props.showMessage(true);
            setTimeout(() => {
                ottInputRef.current?.focus();
            }, 250);
        } catch (e) {
            setFieldError('email', `${constants.EMAIl_ALREADY_OWNED}`);
        }
        setLoading(false);
    };

    const requestEmailChange = async (
        { email, ott }: formValues,
        { setFieldError }: FormikHelpers<formValues>
    ) => {
        try {
            setLoading(true);
            await changeEmail(email, ott);
            setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), email });
            appContext.setDisappearingFlashMessage({
                message: constants.EMAIL_UDPATE_SUCCESSFUL,
                type: FLASH_MESSAGE_TYPE.SUCCESS,
            });
            router.push(PAGES.GALLERY);
        } catch (e) {
            setFieldError('ott', `${constants.INCORRECT_CODE}`);
        }
        setLoading(false);
    };

    return (
        <Formik<formValues>
            initialValues={{ email: '' }}
            validationSchema={Yup.object().shape({
                email: Yup.string()
                    .email(constants.EMAIL_ERROR)
                    .required(constants.REQUIRED),
            })}
            validateOnChange={false}
            validateOnBlur={false}
            onSubmit={!ottInputVisible ? requestOTT : requestEmailChange}>
            {({ values, errors, touched, handleChange, handleSubmit }) => (
                <Form noValidate onSubmit={handleSubmit}>
                    {!ottInputVisible ? (
                        <Form.Group controlId="formBasicEmail">
                            <Form.Control
                                ref={emailInputElement}
                                type="email"
                                placeholder={constants.ENTER_EMAIL}
                                value={values.email}
                                onChange={handleChange('email')}
                                isInvalid={Boolean(
                                    touched.email && errors.email
                                )}
                                autoFocus
                                disabled={loading}
                            />
                            <FormControl.Feedback type="invalid">
                                {errors.email}
                            </FormControl.Feedback>
                        </Form.Group>
                    ) : (
                        <>
                            <EmailRow>
                                <Col xs="8">{values.email}</Col>
                                <Col xs="4">
                                    <Button
                                        variant="link"
                                        onClick={() =>
                                            setShowOttInputVisibility(false)
                                        }>
                                        {constants.CHANGE}
                                    </Button>
                                </Col>
                            </EmailRow>
                            <Form.Group controlId="formBasicEmail">
                                <Form.Control
                                    ref={ottInputRef}
                                    type="text"
                                    placeholder={constants.ENTER_OTT}
                                    value={values.ott}
                                    onChange={handleChange('ott')}
                                    isInvalid={Boolean(
                                        touched.ott && errors.ott
                                    )}
                                    disabled={loading}
                                />
                                <FormControl.Feedback type="invalid">
                                    {errors.ott}
                                </FormControl.Feedback>
                            </Form.Group>
                        </>
                    )}

                    <SubmitButton
                        buttonText={
                            !ottInputVisible
                                ? constants.SEND_OTT
                                : constants.VERIFY
                        }
                        loading={loading}
                    />
                    <br />
                    <Button
                        block
                        variant="link"
                        className="text-center"
                        onClick={router.back}>
                        {constants.GO_BACK}
                    </Button>
                </Form>
            )}
        </Formik>
    );
}
Example #18
Source File: AbuseReportForm.tsx    From bada-frame with GNU General Public License v3.0 4 votes vote down vote up
export function AbuseReportForm({ show, close, url }: Iprops) {
    const [loading, setLoading] = useState(false);
    const appContext = useContext(AppContext);
    const publicCollectionGalleryContent = useContext(
        PublicCollectionGalleryContext
    );

    const submitReport = async (
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        { url, reason, terms, ...details }: FormValues,
        { setFieldError }: FormikHelpers<FormValues>
    ) => {
        try {
            setLoading(true);
            if (reason === REPORT_REASON.MALICIOUS_CONTENT) {
                details.address = undefined;
                details.onBehalfOf = undefined;
                details.jobTitle = undefined;
            }
            if (!details.comment) {
                details.comment = undefined;
            }
            await reportAbuse(
                publicCollectionGalleryContent.token,
                url,
                reason,
                details
            );
            close();
            appContext.setDialogMessage({
                title: constants.REPORT_SUBMIT_SUCCESS_TITLE,
                content: constants.REPORT_SUBMIT_SUCCESS_CONTENT,
                close: { text: constants.OK },
            });
        } catch (e) {
            setFieldError('signature', constants.REPORT_SUBMIT_FAILED);
        } finally {
            setLoading(false);
        }
    };

    return (
        <MessageDialog
            show={show}
            size="lg"
            onHide={close}
            attributes={{
                title: constants.ABUSE_REPORT,
                staticBackdrop: true,
            }}>
            <Wrapper>
                <h6>{constants.ABUSE_REPORT_DESCRIPTION}</h6>
                <Formik<FormValues>
                    initialValues={{
                        ...defaultInitialValues,
                        url,
                    }}
                    validationSchema={Yup.object().shape({
                        reason: Yup.mixed<keyof typeof REPORT_REASON>()
                            .oneOf(Object.values(REPORT_REASON))
                            .required(constants.REQUIRED),
                        url: Yup.string().required(constants.REQUIRED),
                        fullName: Yup.string().required(constants.REQUIRED),
                        email: Yup.string()
                            .email()
                            .required(constants.REQUIRED),
                        comment: Yup.string(),
                        signature: Yup.string().required(constants.REQUIRED),
                        onBehalfOf: Yup.string().when('reason', {
                            is: REPORT_REASON.COPYRIGHT,
                            then: Yup.string().required(constants.REQUIRED),
                        }),
                        jobTitle: Yup.string().when('reason', {
                            is: REPORT_REASON.COPYRIGHT,
                            then: Yup.string().required(constants.REQUIRED),
                        }),
                        address: Yup.object().when('reason', {
                            is: REPORT_REASON.COPYRIGHT,
                            then: Yup.object().shape({
                                city: Yup.string().required(constants.REQUIRED),
                                state: Yup.string().required(
                                    constants.REQUIRED
                                ),
                                country: Yup.string().required(
                                    constants.REQUIRED
                                ),
                                postalCode: Yup.string().required(
                                    constants.REQUIRED
                                ),
                                phone: Yup.string().required(
                                    constants.REQUIRED
                                ),
                            }),
                        }),
                        terms: Yup.object().when('reason', {
                            is: REPORT_REASON.COPYRIGHT,
                            then: Yup.object().shape({
                                1: Yup.boolean(),
                                2: Yup.boolean(),
                                3: Yup.boolean(),
                            }),
                        }),
                    })}
                    validateOnChange={false}
                    validateOnBlur={false}
                    onSubmit={submitReport}>
                    {({
                        values,
                        errors,
                        touched,
                        handleChange,
                        handleSubmit,
                    }): JSX.Element => (
                        <Form noValidate onSubmit={handleSubmit}>
                            <Form.Group controlId="reportForm.url">
                                <Form.Label>{constants.ALBUM_URL}</Form.Label>
                                <Form.Control
                                    type="text"
                                    disabled
                                    value={url}
                                />
                            </Form.Group>
                            <Form.Group controlId="reportForm.reason">
                                <Form.Control
                                    as="select"
                                    value={values.reason}
                                    onChange={handleChange('reason')}
                                    isInvalid={Boolean(
                                        touched.reason && errors.reason
                                    )}
                                    autoFocus
                                    disabled={loading}>
                                    <option disabled selected>
                                        select reason
                                    </option>
                                    {Object.values(REPORT_REASON).map(
                                        (reason) => (
                                            <option key={reason} value={reason}>
                                                {constants[reason]}
                                            </option>
                                        )
                                    )}
                                </Form.Control>
                                <FormControl.Feedback type="invalid">
                                    {constants.SELECT_REASON}
                                </FormControl.Feedback>
                            </Form.Group>
                            <Row>
                                <Col md={6}>
                                    <Form.Group controlId="reportForm.fullName">
                                        <Form.Control
                                            type="text"
                                            placeholder={
                                                constants.ENTER_FULL_NAME
                                            }
                                            value={values.fullName}
                                            onChange={handleChange('fullName')}
                                            isInvalid={Boolean(
                                                touched.fullName &&
                                                    errors.fullName
                                            )}
                                            disabled={loading}
                                        />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.fullName}
                                        </Form.Control.Feedback>
                                    </Form.Group>
                                </Col>
                                <Col md={6}>
                                    <Form.Group controlId="reportForm.email">
                                        <Form.Control
                                            type="text"
                                            placeholder={
                                                constants.ENTER_EMAIL_ADDRESS
                                            }
                                            value={values.email}
                                            onChange={handleChange('email')}
                                            isInvalid={Boolean(
                                                touched.email && errors.email
                                            )}
                                            disabled={loading}
                                        />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.email}
                                        </Form.Control.Feedback>
                                    </Form.Group>
                                </Col>
                            </Row>

                            {values.reason === REPORT_REASON.COPYRIGHT && (
                                <>
                                    <Row>
                                        <Col md={6}>
                                            {' '}
                                            <Form.Group controlId="reportForm.onBehalfOf">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_ON_BEHALF_OF
                                                    }
                                                    value={values.onBehalfOf}
                                                    onChange={handleChange(
                                                        'onBehalfOf'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.onBehalfOf &&
                                                            errors.onBehalfOf
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.onBehalfOf}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                        <Col md={6}>
                                            <Form.Group controlId="reportForm.jobTitle">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_JOB_TITLE
                                                    }
                                                    value={values.jobTitle}
                                                    onChange={handleChange(
                                                        'jobTitle'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.jobTitle &&
                                                            errors.jobTitle
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.jobTitle}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                    </Row>
                                    <Row>
                                        <Col md={6}>
                                            <Form.Group controlId="reportForm.address">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_ADDRESS
                                                    }
                                                    value={
                                                        values.address.street
                                                    }
                                                    onChange={handleChange(
                                                        'address.street'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.address
                                                            ?.street &&
                                                            errors.address
                                                                ?.street
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.address?.street}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                        <Col md={6}>
                                            <Form.Group controlId="reportForm.address.city">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_CITY
                                                    }
                                                    value={values.address.city}
                                                    onChange={handleChange(
                                                        'address.city'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.address?.city &&
                                                            errors.address?.city
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.address?.city}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                    </Row>
                                    <Row>
                                        <Col md={6}>
                                            {' '}
                                            <Form.Group controlId="reportForm.address.phone">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_PHONE
                                                    }
                                                    value={values.address.phone}
                                                    onChange={handleChange(
                                                        'address.phone'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.address
                                                            ?.phone &&
                                                            errors.address
                                                                ?.phone
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.address?.phone}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                        <Col md={6}>
                                            {' '}
                                            <Form.Group controlId="reportForm.address.state">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_STATE
                                                    }
                                                    value={values.address.state}
                                                    onChange={handleChange(
                                                        'address.state'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.address
                                                            ?.state &&
                                                            errors.address
                                                                ?.state
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.address?.state}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                    </Row>
                                    <Row>
                                        <Col md={6}>
                                            {' '}
                                            <Form.Group controlId="reportForm.address.postalCode">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_POSTAL_CODE
                                                    }
                                                    value={
                                                        values.address
                                                            .postalCode
                                                    }
                                                    onChange={handleChange(
                                                        'address.postalCode'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.address
                                                            ?.postalCode &&
                                                            errors.address
                                                                ?.postalCode
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.address?.postalCode}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                        <Col md={6}>
                                            {' '}
                                            <Form.Group controlId="reportForm.address.country">
                                                <Form.Control
                                                    type="text"
                                                    placeholder={
                                                        constants.ENTER_COUNTRY
                                                    }
                                                    value={
                                                        values.address.country
                                                    }
                                                    onChange={handleChange(
                                                        'address.country'
                                                    )}
                                                    isInvalid={Boolean(
                                                        touched.address
                                                            ?.country &&
                                                            errors.address
                                                                ?.country
                                                    )}
                                                    disabled={loading}
                                                />
                                                <Form.Control.Feedback type="invalid">
                                                    {errors.address?.country}
                                                </Form.Control.Feedback>
                                            </Form.Group>
                                        </Col>
                                    </Row>
                                </>
                            )}
                            <Form.Group controlId="reportForm.comment">
                                <Form.Control
                                    type="text-area"
                                    placeholder={constants.COMMENT}
                                    as="textarea"
                                    value={values.comment}
                                    onChange={handleChange('comment')}
                                    isInvalid={Boolean(
                                        touched.comment && errors.comment
                                    )}
                                    disabled={loading}
                                />
                                <Form.Control.Feedback type="invalid">
                                    {errors.comment}
                                </Form.Control.Feedback>
                            </Form.Group>
                            <Form.Group controlId="reportForm.signature">
                                <Form.Control
                                    type="text"
                                    placeholder={
                                        constants.ENTER_DIGITAL_SIGNATURE
                                    }
                                    value={values.signature}
                                    onChange={handleChange('signature')}
                                    isInvalid={Boolean(
                                        touched.signature && errors.signature
                                    )}
                                    disabled={loading}
                                />
                                <Form.Control.Feedback type="invalid">
                                    {errors.signature}
                                </Form.Control.Feedback>
                            </Form.Group>

                            <Wrapper>
                                {values.reason === REPORT_REASON.COPYRIGHT && (
                                    <>
                                        <h6>
                                            {constants.JUDICIAL_DESCRIPTION()}
                                        </h6>
                                        <Form.Group controlId="formBasicCheckbox-1">
                                            <Form.Check
                                                checked={values.terms[1]}
                                                onChange={handleChange(
                                                    'terms[1]'
                                                )}
                                                isInvalid={Boolean(
                                                    touched.terms?.[1] &&
                                                        errors.terms?.[1]
                                                )}
                                                disabled={loading}
                                                type="checkbox"
                                                label={constants.TERM_1}
                                            />
                                        </Form.Group>
                                        <Form.Group controlId="formBasicCheckbox-2">
                                            <Form.Check
                                                checked={values.terms[2]}
                                                onChange={handleChange(
                                                    'terms[2]'
                                                )}
                                                isInvalid={Boolean(
                                                    touched.terms?.[2] &&
                                                        errors.terms?.[2]
                                                )}
                                                disabled={loading}
                                                type="checkbox"
                                                label={constants.TERM_2}
                                            />
                                        </Form.Group>
                                        <Form.Group controlId="formBasicCheckbox-3">
                                            <Form.Check
                                                checked={values.terms[3]}
                                                onChange={handleChange(
                                                    'terms[3]'
                                                )}
                                                isInvalid={Boolean(
                                                    touched.terms?.[3] &&
                                                        errors.terms?.[3]
                                                )}
                                                disabled={loading}
                                                type="checkbox"
                                                label={constants.TERM_3}
                                            />
                                        </Form.Group>
                                    </>
                                )}
                            </Wrapper>
                            <SubmitButton
                                buttonText={constants.SUBMIT}
                                loading={loading}
                                disabled={
                                    values.reason === REPORT_REASON.COPYRIGHT &&
                                    (!values.terms[1] ||
                                        !values.terms[2] ||
                                        !values.terms[3])
                                }
                            />
                        </Form>
                    )}
                </Formik>
            </Wrapper>
        </MessageDialog>
    );
}
Example #19
Source File: index.tsx    From react-bootstrap-country-select with MIT License 4 votes vote down vote up
CountrySelect = ({
  value,
  onChange = () => {},
  onTextChange,
  countries = [ ...COUNTRIES ],
  exclusions,
  additions,
  valueAs = 'object',
  flags = true,
  flush = true,
  disabled = false,
  placeholder = 'Type or select country...',
  noMatchesText = 'No matches',
  size,
  sort, // e.g. (c1, c2) => c1.name < c2.name ? -1 : (c1.name > c2.name ? 1 : 0),
  matchNameFromStart = true,
  matchAbbreviations = false,
  countryLabelFormatter = ({ name }) => name,
  throwInvalidValueError = false, 
  listMaxHeight,
  closeOnSelect = true,
  formControlProps = {},
  overlayProps = {},
  classPrefix = DEFAULT_CLASS_PREFIX,
  className,
}: CountrySelectProps) => {

  const inputGroupRef = useRef(null);
  const formControlRef = useRef(null);
  const hasInitRef = useRef(false);
  const [ width, setWidth ] = useState(-1);

  const [ {
    focused,
    inputText,
    list,
    activeListItemIndex,
    combinedCountries,
  }, dispatch ] = useReducer(reducer, INITIAL_STATE);

  const handleFocus = focus(dispatch);
  const handleBlur = blur(dispatch);
  const handleTextChange = textChange(dispatch);
  const handleListActiveItemChange = activeListItemChange(dispatch);
  const handleCountrySelect = countrySelect(dispatch);
  const handleClear = clear(dispatch);

  const getCountryId = (value: ICountry | string): string => (typeof value === 'string' ? value : value.id);

  const selectedCountry = value ? (combinedCountries || []).find(country => country.id === getCountryId(value)) : null;

  if (throwInvalidValueError && value && !selectedCountry)
    throw new Error(`No matching country for value: ${JSON.stringify(value)}`);

  useEffect(() => {

    if (hasInitRef.current) return;

    const combinedCountries = applyExclusionsAndAdditions(countries, exclusions, additions);

    const sorted = getInitialList(combinedCountries, sort);

    init(dispatch)(sorted);

    hasInitRef.current = true;

  }, [ countries, exclusions, additions, sort ]);

  useEffect(() => {

    setWidth(inputGroupRef.current.offsetWidth);

  }, [ inputGroupRef ]);

  const select = listItemIndex => {

    const country = list[listItemIndex];

    handleCountrySelect();
    onChange(valueAs === 'id' ? country.id : country);

  };

  const escape = () => {

    handleClear();
    onChange(null);

  };

  const inputChange = (text, ev) => {

    if (selectedCountry && flags) {

      text = removeEmojiFlag(text);

    }

    const [ updatedList, updatedActiveListItemIndex ]
      = getUpdatedList(text, list, activeListItemIndex, combinedCountries, sort, matchNameFromStart, matchAbbreviations);

    handleTextChange(text, updatedList, updatedActiveListItemIndex);

    if (onTextChange) onTextChange(text, ev);
    if (value) onChange(null);

  };

  const handleKey = ev => {

    if (ev.key === 'ArrowUp') {

      ev.preventDefault();

      const newIndex = activeListItemIndex <= 0 ? list.length - 1 : activeListItemIndex - 1;
      handleListActiveItemChange(newIndex);

    } else if (ev.key === 'ArrowDown') {

      const newIndex = activeListItemIndex >= list.length - 1 ? 0 : activeListItemIndex + 1;
      handleListActiveItemChange(newIndex);

    } else if (ev.key === 'Enter') {

      if (activeListItemIndex >= 0) select(activeListItemIndex)

    } else if (ev.key === 'Escape') {

      escape();

    }

  };
  
  const classes = classNames([
    className,
    classPrefix,
    flush && `${classPrefix}--flush`,
  ]);

  return (
    <div className={classes}>

      <InputGroup
        ref={inputGroupRef}
        className={`${classPrefix}__input-group`}
        size={size}
      >

        { (!flush && flags) && 
          <InputGroup.Prepend>

            <InputGroup.Text
              className={`${classPrefix}__input-group__flag`}
            >

              {selectedCountry ? selectedCountry.flag : ''}
            
            </InputGroup.Text>
            
          </InputGroup.Prepend>
        }

        <FormControl
          ref={formControlRef}
          className={`${classPrefix}__form-control`}
          value={selectedCountry ? `${flush && flags ? selectedCountry.flag + '   ' : ''}${selectedCountry.name}` : inputText}
          onKeyDown={handleKey}
          onChange={ev => inputChange(ev.target.value, ev)}
          onFocus={handleFocus}
          onBlur={handleBlur}
          placeholder={placeholder}
          disabled={disabled}
          spellCheck={false}
          autoComplete='new-value'
          {...formControlProps}
        />

      </InputGroup>

      <Overlay
        target={inputGroupRef.current}
        rootClose
        placement='bottom-start'
        show={focused && (!selectedCountry || !closeOnSelect)} // experimental; not documented
        onHide={() => {}}
        transition
        {...overlayProps}
      >

        {({ placement, arrowProps, show: _show, popper, ...props }) => (

          <div
            {...props}
            style={{
              width: (width > 0) ? `${width}px` : 'calc(100% - 10px)',
              ...props.style,
            }}
          >

            <OverlayContent
              classPrefix={classPrefix}
              list={list}
              activeListItemIndex={activeListItemIndex}
              countryLabelFormatter={countryLabelFormatter}
              flags={flags}
              noMatchesText={noMatchesText}
              maxHeight={listMaxHeight}
              onListItemClick={select}
            />

          </div>

        )}

      </Overlay>
    
    </div>
  );

}
Example #20
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 4 votes vote down vote up
Bid: React.FC<{
  auction: Auction;
  auctionEnded: boolean;
}> = props => {
  const activeAccount = useAppSelector(state => state.account.activeAccount);
  const { library } = useEthers();
  let { auction, auctionEnded } = props;
  const activeLocale = useActiveLocale();
  const nounsAuctionHouseContract = new NounsAuctionHouseFactory().attach(
    config.addresses.nounsAuctionHouseProxy,
  );

  const account = useAppSelector(state => state.account.activeAccount);

  const bidInputRef = useRef<HTMLInputElement>(null);

  const [bidInput, setBidInput] = useState('');

  const [bidButtonContent, setBidButtonContent] = useState({
    loading: false,
    content: auctionEnded ? <Trans>Settle</Trans> : <Trans>Place bid</Trans>,
  });

  const [showConnectModal, setShowConnectModal] = useState(false);

  const hideModalHandler = () => {
    setShowConnectModal(false);
  };

  const dispatch = useAppDispatch();
  const setModal = useCallback((modal: AlertModal) => dispatch(setAlertModal(modal)), [dispatch]);

  const minBidIncPercentage = useAuctionMinBidIncPercentage();
  const minBid = computeMinimumNextBid(
    auction && new BigNumber(auction.amount.toString()),
    minBidIncPercentage,
  );

  const { send: placeBid, state: placeBidState } = useContractFunction(
    nounsAuctionHouseContract,
    AuctionHouseContractFunction.createBid,
  );
  const { send: settleAuction, state: settleAuctionState } = useContractFunction(
    nounsAuctionHouseContract,
    AuctionHouseContractFunction.settleCurrentAndCreateNewAuction,
  );

  const bidInputHandler = (event: ChangeEvent<HTMLInputElement>) => {
    const input = event.target.value;

    // disable more than 2 digits after decimal point
    if (input.includes('.') && event.target.value.split('.')[1].length > 2) {
      return;
    }

    setBidInput(event.target.value);
  };

  const placeBidHandler = async () => {
    if (!auction || !bidInputRef.current || !bidInputRef.current.value) {
      return;
    }

    if (currentBid(bidInputRef).isLessThan(minBid)) {
      setModal({
        show: true,
        title: <Trans>Insufficient bid amount ?</Trans>,
        message: (
          <Trans>
            Please place a bid higher than or equal to the minimum bid amount of {minBidEth(minBid)}{' '}
            ETH
          </Trans>
        ),
      });
      setBidInput(minBidEth(minBid));
      return;
    }

    const value = utils.parseEther(bidInputRef.current.value.toString());
    const contract = connectContractToSigner(nounsAuctionHouseContract, undefined, library);
    const gasLimit = await contract.estimateGas.createBid(auction.nounId, {
      value,
    });
    placeBid(auction.nounId, {
      value,
      gasLimit: gasLimit.add(10_000), // A 10,000 gas pad is used to avoid 'Out of gas' errors
    });
  };

  const settleAuctionHandler = () => {
    settleAuction();
  };

  const clearBidInput = () => {
    if (bidInputRef.current) {
      bidInputRef.current.value = '';
    }
  };

  // successful bid using redux store state
  useEffect(() => {
    if (!account) return;

    // tx state is mining
    const isMiningUserTx = placeBidState.status === 'Mining';
    // allows user to rebid against themselves so long as it is not the same tx
    const isCorrectTx = currentBid(bidInputRef).isEqualTo(new BigNumber(auction.amount.toString()));
    if (isMiningUserTx && auction.bidder === account && isCorrectTx) {
      placeBidState.status = 'Success';
      setModal({
        title: <Trans>Success</Trans>,
        message: <Trans>Bid was placed successfully!</Trans>,
        show: true,
      });
      setBidButtonContent({ loading: false, content: <Trans>Place bid</Trans> });
      clearBidInput();
    }
  }, [auction, placeBidState, account, setModal]);

  // placing bid transaction state hook
  useEffect(() => {
    switch (!auctionEnded && placeBidState.status) {
      case 'None':
        setBidButtonContent({
          loading: false,
          content: <Trans>Place bid</Trans>,
        });
        break;
      case 'Mining':
        setBidButtonContent({ loading: true, content: <></> });
        break;
      case 'Fail':
        setModal({
          title: <Trans>Transaction Failed</Trans>,
          message: placeBidState?.errorMessage || <Trans>Please try again.</Trans>,
          show: true,
        });
        setBidButtonContent({ loading: false, content: <Trans>Bid</Trans> });
        break;
      case 'Exception':
        setModal({
          title: <Trans>Error</Trans>,
          message: placeBidState?.errorMessage || <Trans>Please try again.</Trans>,
          show: true,
        });
        setBidButtonContent({ loading: false, content: <Trans>Bid</Trans> });
        break;
    }
  }, [placeBidState, auctionEnded, setModal]);

  // settle auction transaction state hook
  useEffect(() => {
    switch (auctionEnded && settleAuctionState.status) {
      case 'None':
        setBidButtonContent({
          loading: false,
          content: <Trans>Settle Auction</Trans>,
        });
        break;
      case 'Mining':
        setBidButtonContent({ loading: true, content: <></> });
        break;
      case 'Success':
        setModal({
          title: <Trans>Success</Trans>,
          message: <Trans>Settled auction successfully!</Trans>,
          show: true,
        });
        setBidButtonContent({ loading: false, content: <Trans>Settle Auction</Trans> });
        break;
      case 'Fail':
        setModal({
          title: <Trans>Transaction Failed</Trans>,
          message: settleAuctionState?.errorMessage || <Trans>Please try again.</Trans>,
          show: true,
        });
        setBidButtonContent({ loading: false, content: <Trans>Settle Auction</Trans> });
        break;
      case 'Exception':
        setModal({
          title: <Trans>Error</Trans>,
          message: settleAuctionState?.errorMessage || <Trans>Please try again.</Trans>,
          show: true,
        });
        setBidButtonContent({ loading: false, content: <Trans>Settle Auction</Trans> });
        break;
    }
  }, [settleAuctionState, auctionEnded, setModal]);

  if (!auction) return null;

  const isDisabled =
    placeBidState.status === 'Mining' || settleAuctionState.status === 'Mining' || !activeAccount;

  const fomoNounsBtnOnClickHandler = () => {
    // Open Fomo Nouns in a new tab
    window.open('https://fomonouns.wtf', '_blank')?.focus();
  };

  const isWalletConnected = activeAccount !== undefined;

  return (
    <>
      {showConnectModal && activeAccount === undefined && (
        <WalletConnectModal onDismiss={hideModalHandler} />
      )}
      <InputGroup>
        {!auctionEnded && (
          <>
            <span className={classes.customPlaceholderBidAmt}>
              {!auctionEnded && !bidInput ? (
                <>
                  Ξ {minBidEth(minBid)}{' '}
                  <span
                    className={
                      activeLocale === 'ja-JP' ? responsiveUiUtilsClasses.disableSmallScreens : ''
                    }
                  >
                    <Trans>or more</Trans>
                  </span>
                </>
              ) : (
                ''
              )}
            </span>
            <FormControl
              className={classes.bidInput}
              type="number"
              min="0"
              onChange={bidInputHandler}
              ref={bidInputRef}
              value={bidInput}
            />
          </>
        )}
        {!auctionEnded ? (
          <Button
            className={auctionEnded ? classes.bidBtnAuctionEnded : classes.bidBtn}
            onClick={auctionEnded ? settleAuctionHandler : placeBidHandler}
            disabled={isDisabled}
          >
            {bidButtonContent.loading ? <Spinner animation="border" /> : bidButtonContent.content}
          </Button>
        ) : (
          <>
            <Col lg={12} className={classes.voteForNextNounBtnWrapper}>
              <Button className={classes.bidBtnAuctionEnded} onClick={fomoNounsBtnOnClickHandler}>
                <Trans>Vote for the next Noun</Trans> ⌐◧-◧
              </Button>
            </Col>
            {/* Only show force settle button if wallet connected */}
            {isWalletConnected && (
              <Col lg={12}>
                <SettleManuallyBtn settleAuctionHandler={settleAuctionHandler} auction={auction} />
              </Col>
            )}
          </>
        )}
      </InputGroup>
    </>
  );
}
Example #21
Source File: PinsView.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function PinsView() {
  const [pinList, setPinList] = useState([])
  const [newVideos, setNewVideos] = useState([])
  const [trendingVideos, setTrendingVideos] = useState([])
  const [showExplorer, setShowExplorer] = useState(false)

  const pid = useRef<any>()

  const updateSearchTables = (community = null, creator = null) => {
    const ids = pinList.map((x) => {
      return x._id
    })
    console.log(ids)
    const params = '?limit=10&ipfsOnly=true'
    let newUrl = `https://3speak.tv/apiv2/feeds/new${params}`
    let trendingUrl = `https://3speak.tv/apiv2/feeds/trending${params}`
    if (community) {
      newUrl = `https://3speak.tv/apiv2/feeds/community/${community}/new${params}`
      trendingUrl = `https://3speak.tv/apiv2/feeds/community/${community}/trending${params}`
    } else if (creator && creator.length > 2) {
      newUrl = `https://3speak.tv/apiv2/feeds/@${creator}`
      trendingUrl = null
    }

    fetch(newUrl)
      .then((r) => r.json())
      .then((r) => {
        for (const video of r) {
          const id = `hive:${video.author}:${video.permlink}`
          video.isPinned = ids.includes(id)
          video.id = id
        }
        console.log(r)
        setNewVideos(r)
      })

    if (!trendingUrl) {
      setTrendingVideos([])
    } else {
      fetch(trendingUrl)
        .then((r) => r.json())
        .then((r) => {
          for (const video of r) {
            const id = `hive:${video.author}:${video.permlink}`
            video.isPinned = ids.includes(id)
            video.id = id
          }
          setTrendingVideos(r)
        })
    }
  }

  const generate = async () => {
    // type error - 2 arguments expected
    setPinList(await PromiseIpc.send('pins.ls', undefined as any))
  }

  const PinLocally = async (cids, title, _id) => {
    debug(`CIDs to store ${JSON.stringify(cids)}`)
    if (cids.length !== 0) {
      NotificationManager.info('Pinning in progress')

      await PromiseIpc.send('pins.add', {
        _id,
        source: 'Pins page',
        cids,
        expire: null,
        meta: {
          title,
        },
      } as any)

      NotificationManager.success(
        `Video with title of ${title} has been successfully pinned! Thank you for contributing!`,
        'Pin Successful',
      )
    } else {
      NotificationManager.warning('This video is not available on IPFS')
    }
    await generate()
  }

  const actionSelect = async (key) => {
    console.log(key)
    switch (key) {
      case '1': {
        const func = () =>
          new Promise(async (resolve, reject) => {
            const ref = React.createRef() as any
            Popup.create({
              content: (
                <div>
                  <Form ref={ref}>
                    <Form.Label>Reflink</Form.Label>
                    <FormControl
                      name="reflink"
                      placeholder="hive:username:123permlink"
                    ></FormControl>
                  </Form>
                </div>
              ),
              buttons: {
                left: [
                  {
                    text: 'Cancel',
                    className: 'secondary',
                    action: function () {
                      Popup.close()
                    },
                  },
                ],
                right: [
                  {
                    text: 'Done',
                    className: 'success',
                    action: function () {
                      resolve(FormUtils.formToObj(new FormData(ref.current)))
                      Popup.close()
                    },
                  },
                ],
              },
            })
          })
        const ret = (await func()) as any
        const video_info = await AccountService.permalinkToVideoInfo(ret.reflink)
        const cids = []
        for (const source of video_info.sources) {
          const url = new (require('url').URL)(source.url)
          try {
            new CID(url.host)
            cids.push(url.host)
          } catch (ex) {
            console.error(ex)
          }
        }
        if (cids.length !== 0) {
          NotificationManager.info('Pinning in progress')
          await PromiseIpc.send('pins.add', {
            _id: ret.reflink,
            source: 'Manual Add',
            cids,
            expire: null,
            meta: {
              title: video_info.title,
            },
          } as any)
          NotificationManager.success(
            `Video with reflink of ${ret.reflink} has been successfully pinned! Thank you for contributing!`,
            'Pin Successful',
          )
        } else {
          NotificationManager.warning('This video is not available on IPFS')
        }
        break
      }
      case '2': {
        NotificationManager.info('GC has started')
        const { ipfs } = await IpfsHandler.getIpfs()
        ipfs.repo.gc()
        break
      }
      default: {
      }
    }
  }

  const removePin = async (reflink) => {
    try {
      await PromiseIpc.send('pins.rm', reflink)
      NotificationManager.success('IPFS pin removal complete')
      await generate()
    } catch (ex) {
      NotificationManager.error('IPFS pin removal resulted in error')
      console.error(ex)
    }
  }

  useEffect(() => {
    document.title = '3Speak - Tokenised video communities'
    void generate()
    pid.current = setInterval(generate, 1500)
    updateSearchTables()

    return () => {
      clearInterval(pid.current)
    }
  }, [])

  const pinRows = useMemo(() => {
    const rows = []
    for (const pin of pinList) {
      const sizeBest = bytesAsString(pin.size)

      rows.push(
        <tr key={pin._id}>
          <td>
            <a href={`#/watch/${pin._id}`}>{pin._id}</a>
            <br />(<strong>{RefLink.parse(pin._id).root}</strong>)
          </td>
          <td>
            <a href={`#/watch/${pin._id}`}>{pin.meta ? pin.meta.title : null} </a>
          </td>
          <td>
            {pin.cids.length > 1 ? (
              <a
                onClick={() => {
                  Popup.create({
                    title: 'CIDs',
                    content: (
                      <div>
                        <Editor value={pin.cids} ace={ace} theme="ace/theme/github"></Editor>
                      </div>
                    ),
                    buttons: {
                      left: [],
                      right: [
                        {
                          text: 'close',
                          key: '⌘+s',
                          className: 'success',
                          action: function () {
                            Popup.close()
                          },
                        },
                      ],
                    },
                  })
                }}
              >
                View ({pin.cids.length})
              </a>
            ) : (
              pin.cids
            )}
          </td>
          <td>{pin.source}</td>
          <td>
            {pin.expire
              ? (() => {
                  console.log(pin.expire)
                  return 'In ' + millisecondsAsString((pin.expire = new Date().getTime()))
                })()
              : 'Permanent'}
          </td>
          <td>
            {pin.meta.pin_date
              ? (() => {
                  console.log(pin.meta.pin_date)
                  return new Date(pin.meta.pin_date).toLocaleString()
                })()
              : null}
          </td>
          <td>{pin.size === 0 ? <strong>Pinning In Progress</strong> : sizeBest}</td>
          <td>
            <Button variant="danger" onClick={() => removePin(pin._id)}>
              X
            </Button>
          </td>
        </tr>,
      )
    }
    return rows
  }, [pinList])

  return (
    <div>
      <Row>
        <Col style={{ textAlign: 'right' }}>
          <Dropdown onSelect={actionSelect}>
            <Dropdown.Toggle as={CustomPinsViewToggle} id="dropdown-custom-components">
              <Button>Actions</Button>
            </Dropdown.Toggle>

            <Dropdown.Menu as={CustomPinsViewMenu}>
              <Dropdown.Item eventKey="1">Manual Pin</Dropdown.Item>
              <Dropdown.Item eventKey="2">Manual GC</Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
        </Col>
      </Row>
      <Table striped bordered hover size="sm">
        <thead>
          <tr>
            <th>Reflink</th>
            <th>Title</th>
            <th>CID(s)</th>
            <th>Source</th>
            <th>Expiration</th>
            <th>Pin Date</th>
            <th>Size/Status</th>
            <th>Remove?</th>
          </tr>
        </thead>
        <tbody>{pinRows}</tbody>
      </Table>
      <Button
        onClick={() => {
          setShowExplorer(!showExplorer)
        }}
      >
        Toggle pin explorer
      </Button>
      {showExplorer && (
        <>
          <h6>Select to pin and help secure the network by backing up videos</h6>
          <input
            type="text"
            placeholder="Enter community ID..."
            onChange={(event) => {
              if (event.target.value.match(/\bhive-\d{6}\b/g)) {
                updateSearchTables(event.target.value, null)
              }
            }}
          />
          <input
            type="text"
            placeholder="Enter a username"
            onChange={(event) => {
              updateSearchTables(null, event.target.value)
            }}
          />
          <Row>
            {['new', 'trending'].map((type: 'new' | 'trending') => (
              <Col key={type}>
                <Table striped bordered hover size="sm">
                  <thead>
                    <tr>
                      <th>{type} videos</th>
                      <th>Title</th>
                      <th>Creator</th>
                      <th>pinned</th>
                    </tr>
                  </thead>
                  <tbody>
                    {/* {this.state[`${type}Videos`].map((video) => ( */}
                    {(type === 'new' ? newVideos : trendingVideos).map((video) => (
                      <tr key={`${type}-${video.author}-${video.permlink}`}>
                        <td>
                          <div className="teaser_holder video-card-image">
                            <div className="card-label">
                              {(() => {
                                const pattern = DateTime.compile('mm:ss')
                                return DateTime.format(new Date(video.duration * 1000), pattern)
                              })()}
                            </div>
                            <a href={`#/watch/hive:${video.author}:${video.permlink}`}>
                              <img
                                className="img-fluid bg-dark"
                                src={video.images.thumbnail}
                                alt=""
                              />
                            </a>
                          </div>
                        </td>
                        <td>{video.title}</td>
                        <td>{video.author}</td>
                        <td>
                          {video.isPinned ? (
                            <Button
                              variant="danger"
                              onClick={async () => {
                                await removePin(video.id)
                                updateSearchTables()
                              }}
                            >
                              X
                            </Button>
                          ) : (
                            <Button
                              variant="success"
                              onClick={async () => {
                                await PinLocally([video.ipfs], video.title, video.id)
                                updateSearchTables()
                              }}
                            >
                              O
                            </Button>
                          )}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </Table>
              </Col>
            ))}
          </Row>
        </>
      )}
    </div>
  )
}
Example #22
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 4 votes vote down vote up
ProposalTransactionFormModal = ({
  show,
  onHide,
  onProposalTransactionAdded,
}: ProposalTransactionFormModalProps) => {
  const [address, setAddress] = useState('');
  const [abi, setABI] = useState<Interface>();
  const [value, setValue] = useState('');
  const [func, setFunction] = useState('');
  const [args, setArguments] = useState<string[]>([]);

  const [isABIUploadValid, setABIUploadValid] = useState<boolean>();
  const [abiFileName, setABIFileName] = useState<string | undefined>('');

  const addressValidator = (s: string) => {
    if (!utils.isAddress(s)) {
      return false;
    }
    // To avoid blocking stepper progress, do not `await`
    populateABIIfExists(s);
    return true;
  };

  const valueValidator = (v: string) => !v || !new BigNumber(v).isNaN();

  const argumentsValidator = (a: string[]) => {
    if (!func) {
      return true;
    }

    try {
      return !!abi?._encodeParams(abi?.functions[func]?.inputs, args);
    } catch {
      return false;
    }
  };

  const setArgument = (index: number, value: string) => {
    const values = [...args];
    values[index] = value;
    setArguments(values);
  };

  let abiErrorTimeout: NodeJS.Timeout;
  const setABIInvalid = () => {
    setABIUploadValid(false);
    setABIFileName(undefined);
    abiErrorTimeout = setTimeout(() => {
      setABIUploadValid(undefined);
    }, 5_000);
  };

  const validateAndSetABI = (file: File | undefined) => {
    if (abiErrorTimeout) {
      clearTimeout(abiErrorTimeout);
    }
    if (!file) {
      return;
    }

    const reader = new FileReader();
    reader.onload = async e => {
      try {
        const abi = e?.target?.result?.toString() ?? '';
        setABI(new Interface(JSON.parse(abi)));
        setABIUploadValid(true);
        setABIFileName(file.name);
      } catch {
        setABIInvalid();
      }
    };
    reader.readAsText(file);
  };

  const getContractInformation = async (address: string) => {
    const response = await fetch(buildEtherscanApiQuery(address));
    const json = await response.json();
    return json?.result?.[0];
  };

  const getABI = async (address: string) => {
    let info = await getContractInformation(address);
    if (info?.Proxy === '1' && utils.isAddress(info?.Implementation)) {
      info = await getContractInformation(info.Implementation);
    }
    return info.ABI;
  };

  const populateABIIfExists = async (address: string) => {
    if (abiErrorTimeout) {
      clearTimeout(abiErrorTimeout);
    }

    try {
      const result = await getABI(address);
      setABI(new Interface(JSON.parse(result)));
      setABIUploadValid(true);
      setABIFileName('etherscan-abi-download.json');
    } catch {
      setABIUploadValid(undefined);
      setABIFileName(undefined);
    }
  };

  const stepForwardOrCallback = () => {
    if (currentStep !== steps.length - 1) {
      return stepForward();
    }
    onProposalTransactionAdded({
      address,
      value: value ? utils.parseEther(value).toString() : '0',
      signature: func,
      calldata: (func && abi?._encodeParams(abi?.functions[func]?.inputs, args)) || '0x',
    });
    clearState();
  };

  const steps = [
    {
      label: 'Address',
      name: 'address',
      validator: () => addressValidator(address),
    },
    {
      label: 'Value',
      name: 'value',
      validator: () => valueValidator(value),
    },
    {
      label: 'Function',
      name: 'function',
    },
    {
      label: 'Arguments',
      name: 'arguments',
      validator: () => argumentsValidator(args),
    },
    {
      label: 'Summary',
      name: 'summary',
    },
  ];

  const { stepForward, stepBackwards, currentStep } = useStepProgress({
    steps,
    startingStep: 0,
  });

  const clearState = () => {
    setAddress('');
    setABI(undefined);
    setValue('');
    setFunction('');
    setArguments([]);
    setABIUploadValid(undefined);
    setABIFileName(undefined);

    for (let i = currentStep; i > 0; i--) {
      stepBackwards();
    }
  };

  return (
    <Modal
      show={show}
      onHide={() => {
        onHide();
        clearState();
      }}
      dialogClassName={classes.transactionFormModal}
      centered
    >
      <Modal.Header closeButton>
        <Modal.Title>
          <Trans>Add a Proposal Transaction</Trans>
        </Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <StepProgressBar className={classes.stepProgressBar} steps={steps} />
        <Step step={0}>
          <label htmlFor="callee-address">
            <Trans>Address (Callee or Recipient)</Trans>
          </label>
          <FormControl
            value={address}
            type="text"
            id="callee-address"
            onChange={e => setAddress(e.target.value)}
          />
        </Step>
        <Step step={1}>
          <label htmlFor="eth-value">
            <Trans>Value in ETH (Optional)</Trans>
          </label>
          <FormControl value={value} id="eth-value" onChange={e => setValue(e.target.value)} />
        </Step>
        <Step step={2}>
          <label htmlFor="function">
            <Trans>Function (Optional)</Trans>
          </label>
          <FormControl
            value={func}
            as="select"
            id="function"
            onChange={e => setFunction(e.target.value)}
          >
            <option className="text-muted">Select Contract Function</option>
            {abi && Object.keys(abi.functions).map(func => <option value={func}>{func}</option>)}
          </FormControl>
          <label style={{ marginTop: '1rem' }} htmlFor="import-abi">
            {abiFileName === 'etherscan-abi-download.json' ? abiFileName : 'ABI'}
          </label>
          <Form.Control
            type="file"
            id="import-abi"
            accept="application/JSON"
            isValid={isABIUploadValid}
            isInvalid={isABIUploadValid === false}
            onChange={(e: ChangeEvent<HTMLInputElement>) => validateAndSetABI(e.target.files?.[0])}
          />
        </Step>
        <Step step={3}>
          {abi?.functions[func]?.inputs?.length ? (
            <FormGroup as={Row}>
              {abi?.functions[func]?.inputs.map((input, i) => (
                <>
                  <FormLabel column sm="3">
                    {input.name}
                  </FormLabel>
                  <Col sm="9">
                    <InputGroup className="mb-2">
                      <InputGroup.Text className={classes.inputGroupText}>
                        {input.type}
                      </InputGroup.Text>
                      <FormControl
                        value={args[i] ?? ''}
                        onChange={e => setArgument(i, e.target.value)}
                      />
                    </InputGroup>
                  </Col>
                </>
              ))}
            </FormGroup>
          ) : (
            <Trans>No arguments required </Trans>
          )}
        </Step>
        <Step step={4}>
          <Row>
            <Col sm="3">
              <b>
                <Trans>Address</Trans>
              </b>
            </Col>
            <Col sm="9" className="text-break">
              <a href={buildEtherscanAddressLink(address)} target="_blank" rel="noreferrer">
                {address}
              </a>
            </Col>
          </Row>
          <Row>
            <Col sm="3">
              <b>
                <Trans>Value</Trans>
              </b>
            </Col>
            <Col sm="9">{value ? `${value} ETH` : <Trans>None</Trans>}</Col>
          </Row>
          <Row>
            <Col sm="3">
              <b>
                <Trans>Function</Trans>
              </b>
            </Col>
            <Col sm="9" className="text-break">
              {func || <Trans>None</Trans>}
            </Col>
          </Row>
          <Row>
            <Col sm="3">
              <b>
                <Trans>Arguments</Trans>
              </b>
            </Col>
            <Col sm="9">
              <hr />
            </Col>
            <Col sm="9">{abi?.functions[func]?.inputs?.length ? '' : <Trans>None</Trans>}</Col>
          </Row>
          {abi?.functions[func]?.inputs.map((input, i) => (
            <Row key={i}>
              <Col sm="3" className={classes.functionName}>
                {i + 1}. {input.name}
              </Col>
              <Col sm="9" className="text-break">
                {args[i]}
              </Col>
            </Row>
          ))}
        </Step>
        <div className="d-flex justify-content-between align-items-center pt-3">
          <Button
            onClick={stepBackwards}
            variant="outline-secondary"
            size="lg"
            disabled={currentStep === 0}
          >
            <Trans>Back</Trans>
          </Button>
          <Button onClick={stepForwardOrCallback} variant="primary" size="lg">
            {currentStep !== steps.length - 1 ? (
              <Trans>Next</Trans>
            ) : (
              <Trans>Add Transaction</Trans>
            )}
          </Button>
        </div>
      </Modal.Body>
    </Modal>
  );
}
Example #23
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 4 votes vote down vote up
VoteModal = ({ show, onHide, proposalId, availableVotes }: VoteModalProps) => {
  const { castVote, castVoteState } = useCastVote();
  const { castVoteWithReason, castVoteWithReasonState } = useCastVoteWithReason();
  const [vote, setVote] = useState<Vote>();
  const [voteReason, setVoteReason] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [isVoteSucessful, setIsVoteSuccessful] = useState(false);
  const [isVoteFailed, setIsVoteFailed] = useState(false);
  const [failureCopy, setFailureCopy] = useState<ReactNode>('');
  const [errorMessage, setErrorMessage] = useState<ReactNode>('');

  const getVoteErrorMessage = (error: string | undefined) => {
    if (error?.match(/voter already voted/)) {
      return <Trans>User Already Voted</Trans>;
    }
    return error;
  };

  const handleVoteStateChange = useCallback((state: TransactionStatus) => {
    switch (state.status) {
      case 'None':
        setIsLoading(false);
        break;
      case 'Mining':
        setIsLoading(true);
        break;
      case 'Success':
        setIsLoading(false);
        setIsVoteSuccessful(true);
        break;
      case 'Fail':
        setFailureCopy(<Trans>Transaction Failed</Trans>);
        setErrorMessage(state?.errorMessage || <Trans>Please try again.</Trans>);
        setIsLoading(false);
        setIsVoteFailed(true);
        break;
      case 'Exception':
        setFailureCopy(<Trans>Error</Trans>);
        setErrorMessage(
          getVoteErrorMessage(state?.errorMessage) || <Trans>Please try again.</Trans>,
        );
        setIsLoading(false);
        setIsVoteFailed(true);
        break;
    }
  }, []);

  // Cast vote transaction state hook
  useEffect(() => {
    handleVoteStateChange(castVoteState);
  }, [castVoteState, handleVoteStateChange]);

  // Cast vote with reason transaction state hook
  useEffect(() => {
    handleVoteStateChange(castVoteWithReasonState);
  }, [castVoteWithReasonState, handleVoteStateChange]);

  // Auto close the modal after a transaction completes succesfully
  // Leave failed transaction up until user closes manually to allow for debugging
  useEffect(() => {
    if (isVoteSucessful) {
      setTimeout(onHide, POST_SUCESSFUL_VOTE_MODAL_CLOSE_TIME_MS);
    }
  }, [isVoteSucessful, onHide]);

  // If show is false (i.e. on hide) reset failure related state variables
  useEffect(() => {
    if (show) {
      return;
    }
    setIsVoteFailed(false);
  }, [show]);

  const voteModalContent = (
    <>
      {isVoteSucessful && (
        <div className={classes.transactionStatus}>
          <p>
            <Trans>
              You've successfully voted on on prop {i18n.number(parseInt(proposalId || '0'))}
            </Trans>
          </p>

          <div className={classes.voteSuccessBody}>
            <Trans>Thank you for voting.</Trans>
          </div>
        </div>
      )}
      {isVoteFailed && (
        <div className={classes.transactionStatus}>
          <p className={classes.voteFailureTitle}>
            <Trans>There was an error voting for your account.</Trans>
          </p>
          <div className={classes.voteFailureBody}>
            {failureCopy}: <span className={classes.voteFailureErrorMessage}>{errorMessage}</span>
          </div>
        </div>
      )}
      {!isVoteFailed && !isVoteSucessful && (
        <div className={clsx(classes.votingButtonsWrapper, isLoading ? classes.disabled : '')}>
          <div onClick={() => setVote(Vote.FOR)}>
            <NavBarButton
              buttonText={
                availableVotes > 1 ? (
                  <Trans>
                    Cast {i18n.number(availableVotes)} votes for Prop{' '}
                    {i18n.number(parseInt(proposalId || '0'))}
                  </Trans>
                ) : (
                  <Trans>Cast 1 vote for Prop {i18n.number(parseInt(proposalId || '0'))}</Trans>
                )
              }
              buttonIcon={<></>}
              buttonStyle={
                vote === Vote.FOR
                  ? NavBarButtonStyle.WHITE_ACTIVE_VOTE_SUBMIT
                  : NavBarButtonStyle.WHITE_INFO
              }
            />
          </div>
          <br />
          <div onClick={() => setVote(Vote.AGAINST)}>
            <NavBarButton
              buttonText={
                availableVotes > 1 ? (
                  <Trans>
                    Cast {i18n.number(availableVotes)} votes against Prop{' '}
                    {i18n.number(parseInt(proposalId || '0'))}
                  </Trans>
                ) : (
                  <Trans>Cast 1 vote against Prop {i18n.number(parseInt(proposalId || '0'))}</Trans>
                )
              }
              buttonIcon={<></>}
              buttonStyle={
                vote === Vote.AGAINST
                  ? NavBarButtonStyle.WHITE_ACTIVE_VOTE_SUBMIT
                  : NavBarButtonStyle.WHITE_INFO
              }
            />
          </div>
          <br />
          <div onClick={() => setVote(Vote.ABSTAIN)}>
            <NavBarButton
              buttonText={
                <Trans>
                  Abstain from voting on Prop {i18n.number(parseInt(proposalId || '0'))}
                </Trans>
              }
              buttonIcon={<></>}
              buttonStyle={
                vote === Vote.ABSTAIN
                  ? NavBarButtonStyle.WHITE_ACTIVE_VOTE_SUBMIT
                  : NavBarButtonStyle.WHITE_INFO
              }
            />
          </div>
          <br />
          <FloatingLabel controlId="reasonTextarea" label={<Trans>Reason (Optional)</Trans>}>
            <FormControl
              as="textarea"
              placeholder={
                i18n.locale === 'en' ? `Reason for voting ${Vote[vote ?? Vote.FOR]}` : ''
              }
              value={voteReason}
              onChange={e => setVoteReason(e.target.value)}
              className={classes.voteReasonTextarea}
            />
          </FloatingLabel>
          <br />
          <Button
            onClick={() => {
              if (vote === undefined || !proposalId || isLoading) {
                return;
              }
              setIsLoading(true);
              if (voteReason.trim() === '') {
                castVote(proposalId, vote);
              } else {
                castVoteWithReason(proposalId, vote, voteReason);
              }
            }}
            className={vote === undefined ? classes.submitBtnDisabled : classes.submitBtn}
          >
            {isLoading ? <Spinner animation="border" /> : <Trans>Submit Vote</Trans>}
          </Button>
        </div>
      )}
    </>
  );

  // On modal dismiss, reset non-success state
  const resetNonSuccessStateAndHideModal = () => {
    setIsLoading(false);
    setIsVoteFailed(false);
    setErrorMessage('');
    setFailureCopy('');
    onHide();
  };

  return (
    <>
      {show && (
        <Modal
          onDismiss={resetNonSuccessStateAndHideModal}
          title={<Trans>Vote on Prop {i18n.number(parseInt(proposalId || '0'))}</Trans>}
          content={voteModalContent}
        />
      )}
    </>
  );
}