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

merge main into v4-c2d

This commit is contained in:
Bogdan Fazakas 2022-06-16 08:25:35 +03:00
commit fe06c2eb2c
9 changed files with 182 additions and 118 deletions

View File

@ -53,7 +53,7 @@ export async function initializeProviderForCompute(
// TODO: Why do we have these one line functions ?!?!?! // TODO: Why do we have these one line functions ?!?!?!
export async function getEncryptedFiles( export async function getEncryptedFiles(
files: FileMetadata[], files: any,
providerUrl: string providerUrl: string
): Promise<string> { ): Promise<string> {
try { try {

View File

@ -20,15 +20,6 @@
text-align: center; text-align: center;
} }
.button:first-child {
margin-left: 0;
}
.button:last-child {
margin-right: 0;
min-width: auto;
}
.button:hover, .button:hover,
.button:focus { .button:focus {
color: var(--brand-white); color: var(--brand-white);

View File

@ -23,6 +23,11 @@
margin-left: calc(var(--spacer) / 4); margin-left: calc(var(--spacer) / 4);
} }
.loader.white {
border-color: rgba(255 255 255 / 0.3);
border-top-color: var(--brand-white);
}
@keyframes loader { @keyframes loader {
to { to {
transform: rotate(360deg); transform: rotate(360deg);

View File

@ -3,12 +3,13 @@ import styles from './index.module.css'
export interface LoaderProps { export interface LoaderProps {
message?: string message?: string
white?: boolean
} }
export default function Loader({ message }: LoaderProps): ReactElement { export default function Loader({ message, white }: LoaderProps): ReactElement {
return ( return (
<div className={styles.loaderWrap}> <div className={styles.loaderWrap}>
<span className={styles.loader} /> <span className={`${styles.loader} ${white ? styles.white : ''}`} />
{message && <span className={styles.message}>{message}</span>} {message && <span className={styles.message}>{message}</span>}
</div> </div>
) )

View File

@ -7,6 +7,7 @@
.actions button { .actions button {
margin: 0 calc(var(--spacer) / 2); margin: 0 calc(var(--spacer) / 2);
min-height: 40px;
} }
.infoIcon { .infoIcon {

View File

@ -10,6 +10,7 @@ import { useRouter } from 'next/router'
import Tooltip from '@shared/atoms/Tooltip' import Tooltip from '@shared/atoms/Tooltip'
import AvailableNetworks from 'src/components/Publish/AvailableNetworks' import AvailableNetworks from 'src/components/Publish/AvailableNetworks'
import Info from '@images/info.svg' import Info from '@images/info.svg'
import Loader from '@shared/atoms/Loader'
export default function Actions({ export default function Actions({
scrollToRef, scrollToRef,
@ -24,8 +25,7 @@ export default function Actions({
values, values,
errors, errors,
isValid, isValid,
isSubmitting, isSubmitting
setFieldValue
}: FormikContextType<FormPublishData> = useFormikContext() }: FormikContextType<FormPublishData> = useFormikContext()
const { connect, accountId } = useWeb3() const { connect, accountId } = useWeb3()
@ -60,6 +60,11 @@ export default function Actions({
(values.user.stepCurrent === 2 && errors.services !== undefined) || (values.user.stepCurrent === 2 && errors.services !== undefined) ||
(values.user.stepCurrent === 3 && errors.pricing !== undefined) (values.user.stepCurrent === 3 && errors.pricing !== undefined)
const hasSubmitError =
values.feedback?.[1].status === 'error' ||
values.feedback?.[2].status === 'error' ||
values.feedback?.[3].status === 'error'
return ( return (
<footer className={styles.actions}> <footer className={styles.actions}>
{did ? ( {did ? (
@ -108,7 +113,13 @@ export default function Actions({
style="primary" style="primary"
disabled={isSubmitting || !isValid} disabled={isSubmitting || !isValid}
> >
Submit {isSubmitting ? (
<Loader white />
) : hasSubmitError ? (
'Retry'
) : (
'Submit'
)}
</Button> </Button>
)} )}
</> </>

View File

@ -1,12 +1,8 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import styles from './index.module.css' import styles from './index.module.css'
import { FormPublishData } from '../_types'
import { useFormikContext } from 'formik'
import { Feedback } from './Feedback' import { Feedback } from './Feedback'
export default function Submission(): ReactElement { export default function Submission(): ReactElement {
const { values, handleSubmit } = useFormikContext<FormPublishData>()
return ( return (
<div className={styles.submission}> <div className={styles.submission}>
<Feedback /> <Feedback />

View File

@ -144,13 +144,18 @@ export async function transformPublishFormToDdo(
} }
// this is the default format hardcoded // this is the default format hardcoded
const file = [ const file = {
{ nftAddress,
type: 'url', datatokenAddress,
url: files[0].url, files: [
method: 'GET' {
} type: 'url',
] index: 0,
url: files[0].url,
method: 'GET'
}
]
}
const filesEncrypted = const filesEncrypted =
!isPreview && !isPreview &&
files?.length && files?.length &&
@ -173,7 +178,7 @@ export async function transformPublishFormToDdo(
'@context': ['https://w3id.org/did/v1'], '@context': ['https://w3id.org/did/v1'],
id: did, id: did,
nftAddress, nftAddress,
version: '4.0.0', version: '4.1.0',
chainId, chainId,
metadata: newMetadata, metadata: newMetadata,
services: [newService], services: [newService],

View File

@ -11,7 +11,7 @@ import Actions from './Actions'
import Debug from './Debug' import Debug from './Debug'
import Navigation from './Navigation' import Navigation from './Navigation'
import { Steps } from './Steps' import { Steps } from './Steps'
import { FormPublishData, PublishFeedback } from './_types' import { FormPublishData } from './_types'
import { useUserPreferences } from '@context/UserPreferences' import { useUserPreferences } from '@context/UserPreferences'
import useNftFactory from '@hooks/contracts/useNftFactory' import useNftFactory from '@hooks/contracts/useNftFactory'
import { ProviderInstance, LoggerInstance, DDO } from '@oceanprotocol/lib' import { ProviderInstance, LoggerInstance, DDO } from '@oceanprotocol/lib'
@ -35,34 +35,34 @@ export default function PublishPage({
const nftFactory = useNftFactory() const nftFactory = useNftFactory()
const newAbortController = useAbortController() const newAbortController = useAbortController()
const [feedback, setFeedback] = useState<PublishFeedback>( // This `feedback` state is auto-synced into Formik context under `values.feedback`
initialPublishFeedback // for use in other components. Syncing defined in ./Steps.tsx child component.
) const [feedback, setFeedback] = useState(initialPublishFeedback)
// Collecting output of each publish step, enabling retry of failed steps
const [erc721Address, setErc721Address] = useState<string>()
const [datatokenAddress, setDatatokenAddress] = useState<string>()
const [ddo, setDdo] = useState<DDO>()
const [ddoEncrypted, setDdoEncrypted] = useState<string>()
const [did, setDid] = useState<string>() const [did, setDid] = useState<string>()
async function handleSubmit(values: FormPublishData) { // --------------------------------------------------
let _erc721Address: string, // 1. Create NFT & datatokens & create pricing schema
_datatokenAddress: string, // --------------------------------------------------
_ddo: DDO, async function create(values: FormPublishData): Promise<{
_encryptedDdo: string erc721Address: string
datatokenAddress: string
}> {
setFeedback((prevState) => ({
...prevState,
'1': {
...prevState['1'],
status: 'active',
errorMessage: null
}
}))
// reset all feedback state
setFeedback(initialPublishFeedback)
// --------------------------------------------------
// 1. Create NFT & datatokens & create pricing schema
// --------------------------------------------------
try { try {
setFeedback((prevState) => ({
...prevState,
'1': {
...prevState['1'],
status: 'active',
txCount: values.pricing.type === 'dynamic' ? 2 : 1,
description: prevState['1'].description
}
}))
const config = getOceanConfig(chainId) const config = getOceanConfig(chainId)
LoggerInstance.log('[publish] using config: ', config) LoggerInstance.log('[publish] using config: ', config)
@ -76,8 +76,7 @@ export default function PublishPage({
) )
const isSuccess = Boolean(erc721Address && datatokenAddress && txHash) const isSuccess = Boolean(erc721Address && datatokenAddress && txHash)
_erc721Address = erc721Address if (!isSuccess) throw new Error('No Token created. Please try again.')
_datatokenAddress = datatokenAddress
LoggerInstance.log('[publish] createTokensAndPricing tx', txHash) LoggerInstance.log('[publish] createTokensAndPricing tx', txHash)
LoggerInstance.log('[publish] erc721Address', erc721Address) LoggerInstance.log('[publish] erc721Address', erc721Address)
@ -87,14 +86,16 @@ export default function PublishPage({
...prevState, ...prevState,
'1': { '1': {
...prevState['1'], ...prevState['1'],
status: isSuccess ? 'success' : 'error', status: 'success',
txHash txHash
} }
})) }))
return { erc721Address, datatokenAddress }
} catch (error) { } catch (error) {
LoggerInstance.error('[publish] error', error.message) LoggerInstance.error('[publish] error', error.message)
if (error.message.length > 65) { if (error.message.length > 65) {
error.message = 'No Token created.' error.message = 'No Token created. Please try again.'
} }
setFeedback((prevState) => ({ setFeedback((prevState) => ({
@ -102,58 +103,65 @@ export default function PublishPage({
'1': { '1': {
...prevState['1'], ...prevState['1'],
status: 'error', status: 'error',
errorMessage: error.message, errorMessage: error.message
description:
values.pricing.type === 'dynamic'
? prevState['1'].description.replace(
'a single transaction',
'a single transaction, after an initial approve transaction'
)
: prevState['1'].description
} }
})) }))
} }
}
// --------------------------------------------------
// 2. Construct and encrypt DDO
// --------------------------------------------------
async function encrypt(
values: FormPublishData,
erc721Address: string,
datatokenAddress: string
): Promise<{ ddo: DDO; ddoEncrypted: string }> {
setFeedback((prevState) => ({
...prevState,
'2': {
...prevState['2'],
status: 'active',
errorMessage: null
}
}))
// --------------------------------------------------
// 2. Construct and encrypt DDO
// --------------------------------------------------
try { try {
setFeedback((prevState) => ({ if (!datatokenAddress || !erc721Address)
...prevState, throw new Error('No NFT or Datatoken received. Please try again.')
'2': {
...prevState['2'],
status: 'active'
}
}))
if (!_datatokenAddress || !_erc721Address)
throw new Error('No NFT or Datatoken received.')
const ddo = await transformPublishFormToDdo( const ddo = await transformPublishFormToDdo(
values, values,
_datatokenAddress, datatokenAddress,
_erc721Address erc721Address
) )
_ddo = ddo if (!ddo) throw new Error('No DDO received. Please try again.')
setDdo(ddo)
LoggerInstance.log('[publish] Got new DDO', ddo) LoggerInstance.log('[publish] Got new DDO', ddo)
const encryptedResponse = await ProviderInstance.encrypt( const ddoEncrypted = await ProviderInstance.encrypt(
ddo, ddo,
values.services[0].providerUrl.url, values.services[0].providerUrl.url,
newAbortController() newAbortController()
) )
const encryptedDdo = encryptedResponse
_encryptedDdo = encryptedDdo if (!ddoEncrypted)
LoggerInstance.log('[publish] Got encrypted DDO', encryptedDdo) throw new Error('No encrypted DDO received. Please try again.')
setDdoEncrypted(ddoEncrypted)
LoggerInstance.log('[publish] Got encrypted DDO', ddoEncrypted)
setFeedback((prevState) => ({ setFeedback((prevState) => ({
...prevState, ...prevState,
'2': { '2': {
...prevState['2'], ...prevState['2'],
status: encryptedDdo ? 'success' : 'error' status: 'success'
} }
})) }))
return { ddo, ddoEncrypted }
} catch (error) { } catch (error) {
LoggerInstance.error('[publish] error', error.message) LoggerInstance.error('[publish] error', error.message)
setFeedback((prevState) => ({ setFeedback((prevState) => ({
@ -165,43 +173,53 @@ export default function PublishPage({
} }
})) }))
} }
}
// --------------------------------------------------
// 3. Write DDO into NFT metadata
// --------------------------------------------------
async function publish(
values: FormPublishData,
ddo: DDO,
ddoEncrypted: string
): Promise<{ did: string }> {
setFeedback((prevState) => ({
...prevState,
'3': {
...prevState['3'],
status: 'active',
errorMessage: null
}
}))
// --------------------------------------------------
// 3. Write DDO into NFT metadata
// --------------------------------------------------
try { try {
setFeedback((prevState) => ({ if (!ddo || !ddoEncrypted)
...prevState, throw new Error('No DDO received. Please try again.')
'3': {
...prevState['3'],
status: 'active'
}
}))
if (!_ddo || !_encryptedDdo) throw new Error('No DDO received.')
const res = await setNFTMetadataAndTokenURI( const res = await setNFTMetadataAndTokenURI(
_ddo, ddo,
accountId, accountId,
web3, web3,
values.metadata.nft, values.metadata.nft,
newAbortController() newAbortController()
) )
if (!res?.transactionHash)
throw new Error(
'Metadata could not be written into the NFT. Please try again.'
)
LoggerInstance.log('[publish] setMetadata result', res) LoggerInstance.log('[publish] setMetadata result', res)
const txHash = res.transactionHash
setFeedback((prevState) => ({ setFeedback((prevState) => ({
...prevState, ...prevState,
'3': { '3': {
...prevState['3'], ...prevState['3'],
status: res ? 'success' : 'error', status: res ? 'success' : 'error',
txHash txHash: res?.transactionHash
} }
})) }))
setDid(_ddo.id) return { did: ddo.id }
} catch (error) { } catch (error) {
LoggerInstance.error('[publish] error', error.message) LoggerInstance.error('[publish] error', error.message)
setFeedback((prevState) => ({ setFeedback((prevState) => ({
@ -215,6 +233,44 @@ export default function PublishPage({
} }
} }
// --------------------------------------------------
// Orchestrate publishing
// --------------------------------------------------
async function handleSubmit(values: FormPublishData) {
// Syncing variables with state, enabling retry of failed steps
let _erc721Address = erc721Address
let _datatokenAddress = datatokenAddress
let _ddo = ddo
let _ddoEncrypted = ddoEncrypted
let _did = did
if (!_erc721Address || !_datatokenAddress) {
const { erc721Address, datatokenAddress } = await create(values)
_erc721Address = erc721Address
_datatokenAddress = datatokenAddress
setErc721Address(erc721Address)
setDatatokenAddress(datatokenAddress)
}
if (!_ddo || !_ddoEncrypted) {
const { ddo, ddoEncrypted } = await encrypt(
values,
_erc721Address,
_datatokenAddress
)
_ddo = ddo
_ddoEncrypted = ddoEncrypted
setDdo(ddo)
setDdoEncrypted(ddoEncrypted)
}
if (!_did) {
const { did } = await publish(values, _ddo, _ddoEncrypted)
_did = did
setDid(did)
}
}
return isInPurgatory && purgatoryData ? null : ( return isInPurgatory && purgatoryData ? null : (
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
@ -224,22 +280,20 @@ export default function PublishPage({
await handleSubmit(values) await handleSubmit(values)
}} }}
> >
{({ values }) => { {({ values }) => (
return ( <>
<> <PageHeader
<PageHeader title={<Title networkId={values.user.chainId} />}
title={<Title networkId={values.user.chainId} />} description={content.description}
description={content.description} />
/> <Form className={styles.form} ref={scrollToRef}>
<Form className={styles.form} ref={scrollToRef}> <Navigation />
<Navigation /> <Steps feedback={feedback} />
<Steps feedback={feedback} /> <Actions scrollToRef={scrollToRef} did={did} />
<Actions scrollToRef={scrollToRef} did={did} /> </Form>
</Form> {debug && <Debug />}
{debug && <Debug />} </>
</> )}
)
}}
</Formik> </Formik>
) )
} }