1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

refactor all the URL inputs

This commit is contained in:
Matthias Kretschmann 2021-11-25 14:16:42 +00:00
parent 1566051545
commit 06dca21d47
Signed by: m
GPG Key ID: 606EEEF3C479A91F
10 changed files with 108 additions and 72 deletions

View File

@ -38,3 +38,7 @@
color: var(--font-color-text); color: var(--font-color-text);
background-color: transparent; background-color: transparent;
} }
.info li.success {
color: var(--brand-alert-green);
}

View File

@ -1,37 +1,25 @@
import React, { ReactElement, useEffect } from 'react' import React, { ReactElement } from 'react'
import { prettySize } from './utils' import { prettySize } from './utils'
import cleanupContentType from '@utils/cleanupContentType' import cleanupContentType from '@utils/cleanupContentType'
import styles from './Info.module.css' import styles from './Info.module.css'
import { useField, useFormikContext } from 'formik'
import { FileMetadata } from '@utils/provider' import { FileMetadata } from '@utils/provider'
export default function FileInfo({ export default function FileInfo({
name, file,
file handleClose
}: { }: {
name: string
file: FileMetadata file: FileMetadata
handleClose(): void
}): ReactElement { }): ReactElement {
const { validateField } = useFormikContext()
const [field, meta, helpers] = useField(name)
// On mount, validate the field manually
useEffect(() => {
validateField(name)
}, [name, validateField])
return ( return (
<div className={styles.info}> <div className={styles.info}>
<h3 className={styles.url}>{(file as any).url}</h3> <h3 className={styles.url}>{(file as any).url}</h3>
<ul> <ul>
<li>URL confirmed</li> <li className={styles.success}> URL confirmed</li>
{file.contentLength && <li>{prettySize(+file.contentLength)}</li>} {file.contentLength && <li>{prettySize(+file.contentLength)}</li>}
{file.contentType && <li>{cleanupContentType(file.contentType)}</li>} {file.contentType && <li>{cleanupContentType(file.contentType)}</li>}
</ul> </ul>
<button <button className={styles.removeButton} onClick={handleClose}>
className={styles.removeButton}
onClick={() => helpers.setValue(undefined)}
>
&times; &times;
</button> </button>
</div> </div>

View File

@ -8,6 +8,7 @@ import { getFileInfo } from '@utils/provider'
import { useWeb3 } from '@context/Web3' import { useWeb3 } from '@context/Web3'
import { getOceanConfig } from '@utils/ocean' import { getOceanConfig } from '@utils/ocean'
import { useCancelToken } from '@hooks/useCancelToken' import { useCancelToken } from '@hooks/useCancelToken'
import { initialValues } from 'src/components/Publish/_constants'
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)
@ -44,17 +45,23 @@ export default function FilesInput(props: InputProps): ReactElement {
loadFileInfo(url) loadFileInfo(url)
} }
function handleClose() {
helpers.setValue(initialValues.services[0].files)
helpers.setTouched(false)
}
return ( return (
<> <>
{field?.value && field.value[0].url !== '' && field.value[0].valid ? ( {field?.value?.length &&
<FileInfo name={props.name} file={field.value[0]} /> field.value[0].url !== '' &&
field.value[0].valid ? (
<FileInfo file={field.value[0]} handleClose={handleClose} />
) : ( ) : (
<UrlInput <UrlInput
submitText="Add File" submitText="Validate"
{...props} {...props}
{...field} name={`${field.name}[0].url`}
name={`${props.name}[0].url`} hasError={Boolean(meta.touched && meta.error)}
value={field?.value && field.value[0].url}
isLoading={isLoading} isLoading={isLoading}
handleButtonClick={handleButtonClick} handleButtonClick={handleButtonClick}
/> />

View File

@ -0,0 +1,9 @@
.error {
composes: error from '@shared/FormInput/index.module.css';
}
.restore {
font-family: var(--font-family-base);
text-transform: none;
font-weight: var(--font-weight-base);
}

View File

@ -1,13 +1,16 @@
import React, { ReactElement, useState } from 'react' import React, { ReactElement, useState } from 'react'
import { useField } from 'formik' import { ErrorMessage, useField } from 'formik'
import UrlInput from '../URLInput' import UrlInput from '../URLInput'
import { useOcean } from '@context/Ocean' import { useOcean } from '@context/Ocean'
import { InputProps } from '@shared/FormInput' import { InputProps } from '@shared/FormInput'
import FileInfo from '../FilesInput/Info'
import styles from './index.module.css'
import Button from '@shared/atoms/Button'
import { initialValues } from 'src/components/Publish/_constants'
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 [isValid, setIsValid] = useState(false)
const { ocean, config } = useOcean() const { ocean, config } = useOcean()
async function validateProvider(url: string) { async function validateProvider(url: string) {
@ -16,31 +19,60 @@ export default function CustomProvider(props: InputProps): ReactElement {
try { try {
// TODO: #948 Remove ocean.provider.isValidProvider dependency. // TODO: #948 Remove ocean.provider.isValidProvider dependency.
const isValid = await ocean.provider.isValidProvider(url) const isValid = await ocean.provider.isValidProvider(url)
setIsValid(isValid) helpers.setValue({ url, valid: isValid })
helpers.setError(undefined) helpers.setError(undefined)
} catch (error) { } catch (error) {
setIsValid(false)
helpers.setError( helpers.setError(
'Could not validate provider. Please check URL and try again' 'Could not validate provider. Please check URL and try again.'
) )
} finally { } finally {
setIsLoading(false) setIsLoading(false)
} }
} }
async function handleButtonClick(e: React.SyntheticEvent, url: string) { async function handleValidateButtonClick(
e: React.SyntheticEvent,
url: string
) {
e.preventDefault() e.preventDefault()
validateProvider(url) validateProvider(url)
} }
return ( function handleFileInfoClose() {
<UrlInput helpers.setValue({ url: '', valid: false })
submitText="Validate" helpers.setTouched(false)
isValid={isValid} }
{...props}
{...field} function handleRestore(e: React.SyntheticEvent) {
isLoading={isLoading} e.preventDefault()
handleButtonClick={handleButtonClick} helpers.setValue(initialValues.services[0].providerUrl)
/> }
return field?.value?.valid ? (
<FileInfo file={field.value} handleClose={handleFileInfoClose} />
) : (
<>
<UrlInput
submitText="Validate"
{...props}
name={`${field.name}.url`}
hasError={Boolean(meta.touched && meta.error)}
isLoading={isLoading}
handleButtonClick={handleValidateButtonClick}
/>
<Button
style="text"
size="small"
onClick={handleRestore}
className={styles.restore}
>
Use Default Provider
</Button>
{typeof meta.error === 'string' && meta.touched && meta.error && (
<div className={styles.error}>
<ErrorMessage name={field.name} />
</div>
)}
</>
) )
} }

View File

@ -11,51 +11,41 @@ export default function URLInput({
handleButtonClick, handleButtonClick,
isLoading, isLoading,
name, name,
value, hasError,
isValid,
...props ...props
}: { }: {
submitText: string submitText: string
handleButtonClick(e: React.SyntheticEvent, data: string): void handleButtonClick(e: React.SyntheticEvent, data: string): void
isLoading: boolean isLoading: boolean
name: string name: string
value: string hasError: boolean
isValid?: boolean
}): ReactElement { }): ReactElement {
const [field, meta] = useField(name) const [field, meta] = useField(name)
const isButtonDisabled = const isButtonDisabled = !field?.value || field.value === ''
!field.value || field.value.length === 0 || field.value === ''
return ( return (
<> <>
<InputGroup> <InputGroup>
<InputElement <InputElement
className={`${styles.input} ${ className={`${styles.input} ${
meta.touched && meta.error ? styles.hasError : '' !isLoading && hasError ? styles.hasError : ''
}`} }`}
{...props} {...props}
name={name} {...field}
value={value}
type="url" type="url"
/> />
{isValid ? ( <Button
<Button size="small" disabled className={styles.success}> style="primary"
Valid size="small"
</Button> onClick={(e: React.SyntheticEvent) => {
) : ( e.preventDefault()
<Button handleButtonClick(e, field.value)
style="primary" }}
size="small" disabled={isButtonDisabled}
onClick={(e: React.SyntheticEvent) => { >
e.preventDefault() {isLoading ? <Loader /> : submitText}
handleButtonClick(e, field.value) </Button>
}}
disabled={isButtonDisabled}
>
{isLoading ? <Loader /> : submitText}
</Button>
)}
</InputGroup> </InputGroup>
{meta.touched && meta.error && ( {meta.touched && meta.error && (

View File

@ -54,8 +54,8 @@ export default function ServicesFields(): ReactElement {
if (!values?.user?.chainId) return if (!values?.user?.chainId) return
const config = getOceanConfig(values.user.chainId) const config = getOceanConfig(values.user.chainId)
config && setFieldValue('services[0].providerUrl', config.providerUri) config && setFieldValue('services[0].providerUrl.url', config.providerUri)
setTouched({ services: [{ providerUrl: true }] }) setTouched({ services: [{ providerUrl: { url: true } }] })
}, [values.user.chainId, setFieldValue, setTouched]) }, [values.user.chainId, setFieldValue, setTouched])
return ( return (

View File

@ -62,7 +62,10 @@ export const initialValues: FormPublishData = {
dataTokenOptions: { name: '', symbol: '' }, dataTokenOptions: { name: '', symbol: '' },
timeout: '', timeout: '',
access: '', access: '',
providerUrl: 'https://provider.mainnet.oceanprotocol.com' providerUrl: {
url: 'https://provider.mainnet.oceanprotocol.com',
valid: true
}
} }
], ],
pricing: { pricing: {

View File

@ -15,7 +15,7 @@ export interface FormPublishService {
timeout: string timeout: string
dataTokenOptions: DataTokenOptions dataTokenOptions: DataTokenOptions
access: 'Download' | 'Compute' | string access: 'Download' | 'Compute' | string
providerUrl?: string providerUrl?: { url: string; valid: boolean }
algorithmPrivacy?: boolean algorithmPrivacy?: boolean
} }

View File

@ -22,7 +22,7 @@ const validationService = {
files: Yup.array<{ url: string; valid: boolean }[]>() files: Yup.array<{ url: string; valid: boolean }[]>()
.of( .of(
Yup.object().shape({ Yup.object().shape({
url: Yup.string().required('Required'), url: Yup.string().url('Must be a valid URL.').required('Required'),
valid: Yup.boolean().isTrue().required('File must be valid.') valid: Yup.boolean().isTrue().required('File must be valid.')
}) })
) )
@ -31,7 +31,7 @@ const validationService = {
links: Yup.array<{ url: string; valid: boolean }[]>() links: Yup.array<{ url: string; valid: boolean }[]>()
.of( .of(
Yup.object().shape({ Yup.object().shape({
url: Yup.string(), url: Yup.string().url('Must be a valid URL.'),
// TODO: require valid file only when URL is given // TODO: require valid file only when URL is given
valid: Yup.boolean() valid: Yup.boolean()
// valid: Yup.boolean().isTrue('File must be valid.') // valid: Yup.boolean().isTrue('File must be valid.')
@ -46,7 +46,10 @@ const validationService = {
access: Yup.string() access: Yup.string()
.matches(/compute|download/g) .matches(/compute|download/g)
.required('Required'), .required('Required'),
providerUrl: Yup.string().url().required('Required') providerUrl: Yup.object().shape({
url: Yup.string().url('Must be a valid URL.').required('Required'),
valid: Yup.boolean().isTrue().required('Valid Provider is required.')
})
} }
const validationPricing = { const validationPricing = {