mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
refactor all the URL inputs
* only act on user action, we were firing events left and right on every keystroke * remove all local state management, the field has the value already * add success state for provider input
This commit is contained in:
parent
5fd97b11e4
commit
5bf8543150
@ -1,56 +1,41 @@
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import { useField } from 'formik'
|
||||
import { toast } from 'react-toastify'
|
||||
import CustomInput from './URLInput/Input'
|
||||
import UrlInput from './URLInput'
|
||||
import { useOcean } from '@context/Ocean'
|
||||
import { InputProps } from '@shared/FormInput'
|
||||
|
||||
export default function CustomProvider(props: InputProps): ReactElement {
|
||||
const [field, meta, helpers] = useField(props.name)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [providerUrl, setProviderUrl] = useState<string>()
|
||||
const [isValid, setIsValid] = useState(false)
|
||||
const { ocean, config } = useOcean()
|
||||
|
||||
function loadProvider() {
|
||||
if (!providerUrl) return
|
||||
async function validateProvider() {
|
||||
let valid: boolean
|
||||
try {
|
||||
setIsLoading(true)
|
||||
valid = await ocean.provider.isValidProvider(providerUrl)
|
||||
} catch (error) {
|
||||
valid = false
|
||||
console.error(error.message)
|
||||
} finally {
|
||||
valid
|
||||
? toast.success('Perfect! That provider URL looks good 🐳')
|
||||
: toast.error(
|
||||
'Could not validate provider. Please check URL and try again'
|
||||
)
|
||||
async function validateProvider(url: string) {
|
||||
setIsLoading(true)
|
||||
|
||||
setIsLoading(false)
|
||||
}
|
||||
try {
|
||||
const isValid = await ocean.provider.isValidProvider(url)
|
||||
setIsValid(isValid)
|
||||
helpers.setError(undefined)
|
||||
} catch (error) {
|
||||
setIsValid(false)
|
||||
helpers.setError(
|
||||
'Could not validate provider. Please check URL and try again'
|
||||
)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
validateProvider()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadProvider()
|
||||
}, [providerUrl, config?.providerUri])
|
||||
|
||||
async function handleButtonClick(e: React.SyntheticEvent, url: string) {
|
||||
helpers.setTouched(false)
|
||||
e.preventDefault()
|
||||
if (providerUrl === url) {
|
||||
loadProvider()
|
||||
}
|
||||
|
||||
setProviderUrl(url)
|
||||
validateProvider(url)
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomInput
|
||||
<UrlInput
|
||||
submitText="Validate"
|
||||
isValid={isValid}
|
||||
{...props}
|
||||
{...field}
|
||||
isLoading={isLoading}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import { useField } from 'formik'
|
||||
import { toast } from 'react-toastify'
|
||||
import FileInfo from './Info'
|
||||
import UrlInput from '../URLInput/Input'
|
||||
import UrlInput from '../URLInput'
|
||||
import { InputProps } from '@shared/FormInput'
|
||||
import { getFileInfo } from '@utils/provider'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
@ -12,22 +12,21 @@ import { useCancelToken } from '@hooks/useCancelToken'
|
||||
export default function FilesInput(props: InputProps): ReactElement {
|
||||
const [field, meta, helpers] = useField(props.name)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [fileUrl, setFileUrl] = useState<string>()
|
||||
const { chainId } = useWeb3()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
function loadFileInfo() {
|
||||
function loadFileInfo(url: string) {
|
||||
const config = getOceanConfig(chainId || 1)
|
||||
|
||||
async function validateUrl() {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const checkedFile = await getFileInfo(
|
||||
fileUrl,
|
||||
url,
|
||||
config?.providerUri,
|
||||
newCancelToken()
|
||||
)
|
||||
checkedFile && helpers.setValue([{ url: fileUrl, ...checkedFile[0] }])
|
||||
checkedFile && helpers.setValue([{ url, ...checkedFile[0] }])
|
||||
} catch (error) {
|
||||
toast.error('Could not fetch file info. Please check URL and try again')
|
||||
console.error(error.message)
|
||||
@ -36,27 +35,13 @@ export default function FilesInput(props: InputProps): ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
fileUrl && validateUrl()
|
||||
validateUrl()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadFileInfo()
|
||||
}, [fileUrl])
|
||||
|
||||
async function handleButtonClick(e: React.SyntheticEvent, url: string) {
|
||||
// hack so the onBlur-triggered validation does not show,
|
||||
// like when this field is required
|
||||
helpers.setTouched(false)
|
||||
|
||||
// File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
|
||||
e.preventDefault()
|
||||
|
||||
// In the case when the user re-add the same URL after it was removed (by accident or intentionally)
|
||||
if (fileUrl === url) {
|
||||
loadFileInfo()
|
||||
}
|
||||
|
||||
setFileUrl(url)
|
||||
loadFileInfo(url)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -10,3 +10,8 @@
|
||||
.error {
|
||||
composes: error from '@shared/FormInput/index.module.css';
|
||||
}
|
||||
|
||||
.success {
|
||||
background: var(--brand-alert-green);
|
||||
opacity: 1 !important;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import Button from '@shared/atoms/Button'
|
||||
import { ErrorMessage, FieldInputProps, useField } from 'formik'
|
||||
import { ErrorMessage, useField } from 'formik'
|
||||
import Loader from '@shared/atoms/Loader'
|
||||
import styles from './Input.module.css'
|
||||
import styles from './index.module.css'
|
||||
import InputGroup from '@shared/FormInput/InputGroup'
|
||||
import InputElement from '@shared/FormInput/InputElement'
|
||||
|
||||
@ -12,6 +12,7 @@ export default function URLInput({
|
||||
isLoading,
|
||||
name,
|
||||
value,
|
||||
isValid,
|
||||
...props
|
||||
}: {
|
||||
submitText: string
|
||||
@ -19,6 +20,7 @@ export default function URLInput({
|
||||
isLoading: boolean
|
||||
name: string
|
||||
value: string
|
||||
isValid?: boolean
|
||||
}): ReactElement {
|
||||
const [field, meta] = useField(name)
|
||||
const isButtonDisabled =
|
||||
@ -36,17 +38,24 @@ export default function URLInput({
|
||||
value={value}
|
||||
type="url"
|
||||
/>
|
||||
<Button
|
||||
style="primary"
|
||||
size="small"
|
||||
onClick={(e: React.SyntheticEvent) => {
|
||||
e.preventDefault()
|
||||
handleButtonClick(e, field.value)
|
||||
}}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{isLoading ? <Loader /> : submitText}
|
||||
</Button>
|
||||
|
||||
{isValid ? (
|
||||
<Button size="small" disabled className={styles.success}>
|
||||
✓ Valid
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
style="primary"
|
||||
size="small"
|
||||
onClick={(e: React.SyntheticEvent) => {
|
||||
e.preventDefault()
|
||||
handleButtonClick(e, field.value)
|
||||
}}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{isLoading ? <Loader /> : submitText}
|
||||
</Button>
|
||||
)}
|
||||
</InputGroup>
|
||||
|
||||
{meta.touched && meta.error && (
|
@ -6,6 +6,8 @@ import IconCompute from '@images/compute.svg'
|
||||
import content from '../../../../content/publish/form.json'
|
||||
import { getFieldContent } from '../_utils'
|
||||
import { FormPublishData } from '../_types'
|
||||
import { useWeb3 } from '@context/Web3'
|
||||
import { getOceanConfig } from '@utils/ocean'
|
||||
|
||||
const accessTypeOptionsTitles = getFieldContent(
|
||||
'access',
|
||||
@ -14,7 +16,8 @@ const accessTypeOptionsTitles = getFieldContent(
|
||||
|
||||
export default function ServicesFields(): ReactElement {
|
||||
// connect with Form state, use for conditional field rendering
|
||||
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||
const { values, setFieldValue, setTouched } =
|
||||
useFormikContext<FormPublishData>()
|
||||
|
||||
const accessTypeOptions = [
|
||||
{
|
||||
@ -44,7 +47,16 @@ export default function ServicesFields(): ReactElement {
|
||||
'services[0].access',
|
||||
values.services[0].algorithmPrivacy === true ? 'compute' : 'download'
|
||||
)
|
||||
}, [values.services[0].algorithmPrivacy])
|
||||
}, [values.services[0].algorithmPrivacy, setFieldValue])
|
||||
|
||||
// Auto-change default providerUrl on user network change
|
||||
useEffect(() => {
|
||||
if (!values?.user?.chainId) return
|
||||
|
||||
const config = getOceanConfig(values.user.chainId)
|
||||
config && setFieldValue('services[0].providerUrl', config.providerUri)
|
||||
setTouched({ services: [{ providerUrl: true }] })
|
||||
}, [values.user.chainId, setFieldValue, setTouched])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -56,7 +56,7 @@ export const initialValues: FormPublishData = {
|
||||
dataTokenOptions: { name: '', symbol: '' },
|
||||
timeout: '',
|
||||
access: '',
|
||||
providerUrl: 'https://provider.oceanprotocol.com'
|
||||
providerUrl: 'https://provider.mainnet.oceanprotocol.com'
|
||||
}
|
||||
],
|
||||
pricing: {
|
||||
|
Loading…
Reference in New Issue
Block a user