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 { useField } from 'formik'
|
||||||
import { toast } from 'react-toastify'
|
import UrlInput from './URLInput'
|
||||||
import CustomInput from './URLInput/Input'
|
|
||||||
import { useOcean } from '@context/Ocean'
|
import { useOcean } from '@context/Ocean'
|
||||||
import { InputProps } from '@shared/FormInput'
|
import { InputProps } from '@shared/FormInput'
|
||||||
|
|
||||||
export default function CustomProvider(props: InputProps): ReactElement {
|
export default function CustomProvider(props: InputProps): ReactElement {
|
||||||
const [field, meta, helpers] = useField(props.name)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [providerUrl, setProviderUrl] = useState<string>()
|
const [isValid, setIsValid] = useState(false)
|
||||||
const { ocean, config } = useOcean()
|
const { ocean, config } = useOcean()
|
||||||
|
|
||||||
function loadProvider() {
|
async function validateProvider(url: string) {
|
||||||
if (!providerUrl) return
|
setIsLoading(true)
|
||||||
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'
|
|
||||||
)
|
|
||||||
|
|
||||||
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) {
|
async function handleButtonClick(e: React.SyntheticEvent, url: string) {
|
||||||
helpers.setTouched(false)
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (providerUrl === url) {
|
validateProvider(url)
|
||||||
loadProvider()
|
|
||||||
}
|
|
||||||
|
|
||||||
setProviderUrl(url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomInput
|
<UrlInput
|
||||||
submitText="Validate"
|
submitText="Validate"
|
||||||
|
isValid={isValid}
|
||||||
{...props}
|
{...props}
|
||||||
{...field}
|
{...field}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { ReactElement, useState, useEffect } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { useField } from 'formik'
|
import { useField } from 'formik'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import FileInfo from './Info'
|
import FileInfo from './Info'
|
||||||
import UrlInput from '../URLInput/Input'
|
import UrlInput from '../URLInput'
|
||||||
import { InputProps } from '@shared/FormInput'
|
import { InputProps } from '@shared/FormInput'
|
||||||
import { getFileInfo } from '@utils/provider'
|
import { getFileInfo } from '@utils/provider'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
@ -12,22 +12,21 @@ import { useCancelToken } from '@hooks/useCancelToken'
|
|||||||
export default function FilesInput(props: InputProps): ReactElement {
|
export default function FilesInput(props: InputProps): ReactElement {
|
||||||
const [field, meta, helpers] = useField(props.name)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [fileUrl, setFileUrl] = useState<string>()
|
|
||||||
const { chainId } = useWeb3()
|
const { chainId } = useWeb3()
|
||||||
const newCancelToken = useCancelToken()
|
const newCancelToken = useCancelToken()
|
||||||
|
|
||||||
function loadFileInfo() {
|
function loadFileInfo(url: string) {
|
||||||
const config = getOceanConfig(chainId || 1)
|
const config = getOceanConfig(chainId || 1)
|
||||||
|
|
||||||
async function validateUrl() {
|
async function validateUrl() {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const checkedFile = await getFileInfo(
|
const checkedFile = await getFileInfo(
|
||||||
fileUrl,
|
url,
|
||||||
config?.providerUri,
|
config?.providerUri,
|
||||||
newCancelToken()
|
newCancelToken()
|
||||||
)
|
)
|
||||||
checkedFile && helpers.setValue([{ url: fileUrl, ...checkedFile[0] }])
|
checkedFile && helpers.setValue([{ url, ...checkedFile[0] }])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Could not fetch file info. Please check URL and try again')
|
toast.error('Could not fetch file info. Please check URL and try again')
|
||||||
console.error(error.message)
|
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) {
|
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'
|
// File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
loadFileInfo(url)
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,3 +10,8 @@
|
|||||||
.error {
|
.error {
|
||||||
composes: error from '@shared/FormInput/index.module.css';
|
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 React, { ReactElement } from 'react'
|
||||||
import Button from '@shared/atoms/Button'
|
import Button from '@shared/atoms/Button'
|
||||||
import { ErrorMessage, FieldInputProps, useField } from 'formik'
|
import { ErrorMessage, useField } from 'formik'
|
||||||
import Loader from '@shared/atoms/Loader'
|
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 InputGroup from '@shared/FormInput/InputGroup'
|
||||||
import InputElement from '@shared/FormInput/InputElement'
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ export default function URLInput({
|
|||||||
isLoading,
|
isLoading,
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
|
isValid,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
submitText: string
|
submitText: string
|
||||||
@ -19,6 +20,7 @@ export default function URLInput({
|
|||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
name: string
|
name: string
|
||||||
value: string
|
value: string
|
||||||
|
isValid?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [field, meta] = useField(name)
|
const [field, meta] = useField(name)
|
||||||
const isButtonDisabled =
|
const isButtonDisabled =
|
||||||
@ -36,17 +38,24 @@ export default function URLInput({
|
|||||||
value={value}
|
value={value}
|
||||||
type="url"
|
type="url"
|
||||||
/>
|
/>
|
||||||
<Button
|
|
||||||
style="primary"
|
{isValid ? (
|
||||||
size="small"
|
<Button size="small" disabled className={styles.success}>
|
||||||
onClick={(e: React.SyntheticEvent) => {
|
✓ Valid
|
||||||
e.preventDefault()
|
</Button>
|
||||||
handleButtonClick(e, field.value)
|
) : (
|
||||||
}}
|
<Button
|
||||||
disabled={isButtonDisabled}
|
style="primary"
|
||||||
>
|
size="small"
|
||||||
{isLoading ? <Loader /> : submitText}
|
onClick={(e: React.SyntheticEvent) => {
|
||||||
</Button>
|
e.preventDefault()
|
||||||
|
handleButtonClick(e, field.value)
|
||||||
|
}}
|
||||||
|
disabled={isButtonDisabled}
|
||||||
|
>
|
||||||
|
{isLoading ? <Loader /> : submitText}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
{meta.touched && meta.error && (
|
{meta.touched && meta.error && (
|
@ -6,6 +6,8 @@ import IconCompute from '@images/compute.svg'
|
|||||||
import content from '../../../../content/publish/form.json'
|
import content from '../../../../content/publish/form.json'
|
||||||
import { getFieldContent } from '../_utils'
|
import { getFieldContent } from '../_utils'
|
||||||
import { FormPublishData } from '../_types'
|
import { FormPublishData } from '../_types'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import { getOceanConfig } from '@utils/ocean'
|
||||||
|
|
||||||
const accessTypeOptionsTitles = getFieldContent(
|
const accessTypeOptionsTitles = getFieldContent(
|
||||||
'access',
|
'access',
|
||||||
@ -14,7 +16,8 @@ const accessTypeOptionsTitles = getFieldContent(
|
|||||||
|
|
||||||
export default function ServicesFields(): ReactElement {
|
export default function ServicesFields(): ReactElement {
|
||||||
// connect with Form state, use for conditional field rendering
|
// connect with Form state, use for conditional field rendering
|
||||||
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
const { values, setFieldValue, setTouched } =
|
||||||
|
useFormikContext<FormPublishData>()
|
||||||
|
|
||||||
const accessTypeOptions = [
|
const accessTypeOptions = [
|
||||||
{
|
{
|
||||||
@ -44,7 +47,16 @@ export default function ServicesFields(): ReactElement {
|
|||||||
'services[0].access',
|
'services[0].access',
|
||||||
values.services[0].algorithmPrivacy === true ? 'compute' : 'download'
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -56,7 +56,7 @@ export const initialValues: FormPublishData = {
|
|||||||
dataTokenOptions: { name: '', symbol: '' },
|
dataTokenOptions: { name: '', symbol: '' },
|
||||||
timeout: '',
|
timeout: '',
|
||||||
access: '',
|
access: '',
|
||||||
providerUrl: 'https://provider.oceanprotocol.com'
|
providerUrl: 'https://provider.mainnet.oceanprotocol.com'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
pricing: {
|
pricing: {
|
||||||
|
Loading…
Reference in New Issue
Block a user