oceanprotocol/market

View on GitHub
src/components/@shared/FormInput/InputElement/Provider/index.tsx

Summary

Maintainability
A
45 mins
Test Coverage
F
25%
import React, { ReactElement, useState } from 'react'
import { useField, useFormikContext } from 'formik'
import UrlInput from '../URLInput'
import { InputProps } from '@shared/FormInput'
import FileInfo from '../FilesInput/Info'
import styles from './index.module.css'
import Button from '@shared/atoms/Button'
import {
  LoggerInstance,
  ProviderInstance,
  getErrorMessage
} from '@oceanprotocol/lib'
import { FormPublishData } from '@components/Publish/_types'
import { getOceanConfig } from '@utils/ocean'
import axios from 'axios'
import { useCancelToken } from '@hooks/useCancelToken'
import { useNetwork } from 'wagmi'
import { toast } from 'react-toastify'

export default function CustomProvider(props: InputProps): ReactElement {
  const { chain } = useNetwork()
  const newCancelToken = useCancelToken()
  const { initialValues, setFieldError } = useFormikContext<FormPublishData>()
  const [field, meta, helpers] = useField(props.name)
  const [isLoading, setIsLoading] = useState(false)

  async function handleValidation(e: React.SyntheticEvent) {
    e.preventDefault()

    try {
      setIsLoading(true)

      // Check if provider is a valid provider
      const isValid = await ProviderInstance.isValidProvider(field.value.url)

      // No way to detect a failed request with ProviderInstance.isValidProvider,
      // making this error show up for multiple cases it shouldn't, like network
      // down.
      if (!isValid) {
        setFieldError(
          `${field.name}.url`,
          '✗ No valid provider detected. Check your network, your URL and try again.'
        )
        LoggerInstance.error(
          '[Custom Provider]:',
          '✗ No valid provider detected. Check your network, your URL and try again.'
        )
        return
      }

      // Check if valid provider is for same chain user is on
      const providerResponse = await axios.get(field.value.url, {
        cancelToken: newCancelToken()
      })
      const userChainId = chain?.id || 1
      const providerChain =
        providerResponse?.data?.chainId || providerResponse?.data?.chainIds

      const isCompatible =
        providerChain === userChainId
          ? true
          : !!(providerChain.length > 0 && providerChain.includes(userChainId))

      if (!isCompatible) {
        setFieldError(
          `${field.name}.url`,
          '✗ This provider is incompatible with the network your wallet is connected to.'
        )
        LoggerInstance.error(
          '[Custom Provider]:',
          '✗ This provider is incompatible with the network your wallet is connected to.'
        )
        return
      }

      // if all good, add provider to formik state
      helpers.setValue({ url: field.value.url, valid: isValid, custom: true })
    } catch (error) {
      const message = getErrorMessage(error.message)
      setFieldError(`${field.name}.url`, message)
      LoggerInstance.error('[Custom Provider]:', message)
    } finally {
      setIsLoading(false)
    }
  }

  function handleFileInfoClose() {
    helpers.setValue({ url: '', valid: false, custom: true })
    helpers.setTouched(false)
  }

  function handleDefault(e: React.SyntheticEvent) {
    e.preventDefault()

    const oceanConfig = getOceanConfig(chain?.id)
    const providerUrl =
      oceanConfig?.providerUri || initialValues.services[0].providerUrl.url

    helpers.setValue({ url: providerUrl, valid: true, custom: false })
  }

  return field?.value?.valid === true ? (
    <FileInfo file={field.value} handleClose={handleFileInfoClose} />
  ) : (
    <>
      <UrlInput
        submitText="Validate"
        {...props}
        name={`${field.name}.url`}
        isLoading={isLoading}
        handleButtonClick={handleValidation}
      />
      <Button
        style="text"
        size="small"
        onClick={handleDefault}
        className={styles.default}
      >
        Use Default Provider
      </Button>
    </>
  )
}