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:
commit
fe06c2eb2c
@ -53,7 +53,7 @@ export async function initializeProviderForCompute(
|
||||
|
||||
// TODO: Why do we have these one line functions ?!?!?!
|
||||
export async function getEncryptedFiles(
|
||||
files: FileMetadata[],
|
||||
files: any,
|
||||
providerUrl: string
|
||||
): Promise<string> {
|
||||
try {
|
||||
|
@ -20,15 +20,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.button:last-child {
|
||||
margin-right: 0;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus {
|
||||
color: var(--brand-white);
|
||||
|
@ -23,6 +23,11 @@
|
||||
margin-left: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.loader.white {
|
||||
border-color: rgba(255 255 255 / 0.3);
|
||||
border-top-color: var(--brand-white);
|
||||
}
|
||||
|
||||
@keyframes loader {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
|
@ -3,12 +3,13 @@ import styles from './index.module.css'
|
||||
|
||||
export interface LoaderProps {
|
||||
message?: string
|
||||
white?: boolean
|
||||
}
|
||||
|
||||
export default function Loader({ message }: LoaderProps): ReactElement {
|
||||
export default function Loader({ message, white }: LoaderProps): ReactElement {
|
||||
return (
|
||||
<div className={styles.loaderWrap}>
|
||||
<span className={styles.loader} />
|
||||
<span className={`${styles.loader} ${white ? styles.white : ''}`} />
|
||||
{message && <span className={styles.message}>{message}</span>}
|
||||
</div>
|
||||
)
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
.actions button {
|
||||
margin: 0 calc(var(--spacer) / 2);
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.infoIcon {
|
||||
|
@ -10,6 +10,7 @@ import { useRouter } from 'next/router'
|
||||
import Tooltip from '@shared/atoms/Tooltip'
|
||||
import AvailableNetworks from 'src/components/Publish/AvailableNetworks'
|
||||
import Info from '@images/info.svg'
|
||||
import Loader from '@shared/atoms/Loader'
|
||||
|
||||
export default function Actions({
|
||||
scrollToRef,
|
||||
@ -24,8 +25,7 @@ export default function Actions({
|
||||
values,
|
||||
errors,
|
||||
isValid,
|
||||
isSubmitting,
|
||||
setFieldValue
|
||||
isSubmitting
|
||||
}: FormikContextType<FormPublishData> = useFormikContext()
|
||||
const { connect, accountId } = useWeb3()
|
||||
|
||||
@ -60,6 +60,11 @@ export default function Actions({
|
||||
(values.user.stepCurrent === 2 && errors.services !== 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 (
|
||||
<footer className={styles.actions}>
|
||||
{did ? (
|
||||
@ -108,7 +113,13 @@ export default function Actions({
|
||||
style="primary"
|
||||
disabled={isSubmitting || !isValid}
|
||||
>
|
||||
Submit
|
||||
{isSubmitting ? (
|
||||
<Loader white />
|
||||
) : hasSubmitError ? (
|
||||
'Retry'
|
||||
) : (
|
||||
'Submit'
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
|
@ -1,12 +1,8 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import styles from './index.module.css'
|
||||
import { FormPublishData } from '../_types'
|
||||
import { useFormikContext } from 'formik'
|
||||
import { Feedback } from './Feedback'
|
||||
|
||||
export default function Submission(): ReactElement {
|
||||
const { values, handleSubmit } = useFormikContext<FormPublishData>()
|
||||
|
||||
return (
|
||||
<div className={styles.submission}>
|
||||
<Feedback />
|
||||
|
@ -144,13 +144,18 @@ export async function transformPublishFormToDdo(
|
||||
}
|
||||
|
||||
// this is the default format hardcoded
|
||||
const file = [
|
||||
{
|
||||
type: 'url',
|
||||
url: files[0].url,
|
||||
method: 'GET'
|
||||
}
|
||||
]
|
||||
const file = {
|
||||
nftAddress,
|
||||
datatokenAddress,
|
||||
files: [
|
||||
{
|
||||
type: 'url',
|
||||
index: 0,
|
||||
url: files[0].url,
|
||||
method: 'GET'
|
||||
}
|
||||
]
|
||||
}
|
||||
const filesEncrypted =
|
||||
!isPreview &&
|
||||
files?.length &&
|
||||
@ -173,7 +178,7 @@ export async function transformPublishFormToDdo(
|
||||
'@context': ['https://w3id.org/did/v1'],
|
||||
id: did,
|
||||
nftAddress,
|
||||
version: '4.0.0',
|
||||
version: '4.1.0',
|
||||
chainId,
|
||||
metadata: newMetadata,
|
||||
services: [newService],
|
||||
|
@ -11,7 +11,7 @@ import Actions from './Actions'
|
||||
import Debug from './Debug'
|
||||
import Navigation from './Navigation'
|
||||
import { Steps } from './Steps'
|
||||
import { FormPublishData, PublishFeedback } from './_types'
|
||||
import { FormPublishData } from './_types'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import useNftFactory from '@hooks/contracts/useNftFactory'
|
||||
import { ProviderInstance, LoggerInstance, DDO } from '@oceanprotocol/lib'
|
||||
@ -35,34 +35,34 @@ export default function PublishPage({
|
||||
const nftFactory = useNftFactory()
|
||||
const newAbortController = useAbortController()
|
||||
|
||||
const [feedback, setFeedback] = useState<PublishFeedback>(
|
||||
initialPublishFeedback
|
||||
)
|
||||
// This `feedback` state is auto-synced into Formik context under `values.feedback`
|
||||
// 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>()
|
||||
|
||||
async function handleSubmit(values: FormPublishData) {
|
||||
let _erc721Address: string,
|
||||
_datatokenAddress: string,
|
||||
_ddo: DDO,
|
||||
_encryptedDdo: string
|
||||
// --------------------------------------------------
|
||||
// 1. Create NFT & datatokens & create pricing schema
|
||||
// --------------------------------------------------
|
||||
async function create(values: FormPublishData): Promise<{
|
||||
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 {
|
||||
setFeedback((prevState) => ({
|
||||
...prevState,
|
||||
'1': {
|
||||
...prevState['1'],
|
||||
status: 'active',
|
||||
txCount: values.pricing.type === 'dynamic' ? 2 : 1,
|
||||
description: prevState['1'].description
|
||||
}
|
||||
}))
|
||||
|
||||
const config = getOceanConfig(chainId)
|
||||
LoggerInstance.log('[publish] using config: ', config)
|
||||
|
||||
@ -76,8 +76,7 @@ export default function PublishPage({
|
||||
)
|
||||
|
||||
const isSuccess = Boolean(erc721Address && datatokenAddress && txHash)
|
||||
_erc721Address = erc721Address
|
||||
_datatokenAddress = datatokenAddress
|
||||
if (!isSuccess) throw new Error('No Token created. Please try again.')
|
||||
|
||||
LoggerInstance.log('[publish] createTokensAndPricing tx', txHash)
|
||||
LoggerInstance.log('[publish] erc721Address', erc721Address)
|
||||
@ -87,14 +86,16 @@ export default function PublishPage({
|
||||
...prevState,
|
||||
'1': {
|
||||
...prevState['1'],
|
||||
status: isSuccess ? 'success' : 'error',
|
||||
status: 'success',
|
||||
txHash
|
||||
}
|
||||
}))
|
||||
|
||||
return { erc721Address, datatokenAddress }
|
||||
} catch (error) {
|
||||
LoggerInstance.error('[publish] error', error.message)
|
||||
if (error.message.length > 65) {
|
||||
error.message = 'No Token created.'
|
||||
error.message = 'No Token created. Please try again.'
|
||||
}
|
||||
|
||||
setFeedback((prevState) => ({
|
||||
@ -102,58 +103,65 @@ export default function PublishPage({
|
||||
'1': {
|
||||
...prevState['1'],
|
||||
status: 'error',
|
||||
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
|
||||
errorMessage: error.message
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 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 {
|
||||
setFeedback((prevState) => ({
|
||||
...prevState,
|
||||
'2': {
|
||||
...prevState['2'],
|
||||
status: 'active'
|
||||
}
|
||||
}))
|
||||
|
||||
if (!_datatokenAddress || !_erc721Address)
|
||||
throw new Error('No NFT or Datatoken received.')
|
||||
if (!datatokenAddress || !erc721Address)
|
||||
throw new Error('No NFT or Datatoken received. Please try again.')
|
||||
|
||||
const ddo = await transformPublishFormToDdo(
|
||||
values,
|
||||
_datatokenAddress,
|
||||
_erc721Address
|
||||
datatokenAddress,
|
||||
erc721Address
|
||||
)
|
||||
|
||||
_ddo = ddo
|
||||
if (!ddo) throw new Error('No DDO received. Please try again.')
|
||||
|
||||
setDdo(ddo)
|
||||
LoggerInstance.log('[publish] Got new DDO', ddo)
|
||||
|
||||
const encryptedResponse = await ProviderInstance.encrypt(
|
||||
const ddoEncrypted = await ProviderInstance.encrypt(
|
||||
ddo,
|
||||
values.services[0].providerUrl.url,
|
||||
newAbortController()
|
||||
)
|
||||
const encryptedDdo = encryptedResponse
|
||||
_encryptedDdo = encryptedDdo
|
||||
LoggerInstance.log('[publish] Got encrypted DDO', encryptedDdo)
|
||||
|
||||
if (!ddoEncrypted)
|
||||
throw new Error('No encrypted DDO received. Please try again.')
|
||||
|
||||
setDdoEncrypted(ddoEncrypted)
|
||||
LoggerInstance.log('[publish] Got encrypted DDO', ddoEncrypted)
|
||||
|
||||
setFeedback((prevState) => ({
|
||||
...prevState,
|
||||
'2': {
|
||||
...prevState['2'],
|
||||
status: encryptedDdo ? 'success' : 'error'
|
||||
status: 'success'
|
||||
}
|
||||
}))
|
||||
|
||||
return { ddo, ddoEncrypted }
|
||||
} catch (error) {
|
||||
LoggerInstance.error('[publish] error', error.message)
|
||||
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 {
|
||||
setFeedback((prevState) => ({
|
||||
...prevState,
|
||||
'3': {
|
||||
...prevState['3'],
|
||||
status: 'active'
|
||||
}
|
||||
}))
|
||||
|
||||
if (!_ddo || !_encryptedDdo) throw new Error('No DDO received.')
|
||||
if (!ddo || !ddoEncrypted)
|
||||
throw new Error('No DDO received. Please try again.')
|
||||
|
||||
const res = await setNFTMetadataAndTokenURI(
|
||||
_ddo,
|
||||
ddo,
|
||||
accountId,
|
||||
web3,
|
||||
values.metadata.nft,
|
||||
newAbortController()
|
||||
)
|
||||
if (!res?.transactionHash)
|
||||
throw new Error(
|
||||
'Metadata could not be written into the NFT. Please try again.'
|
||||
)
|
||||
|
||||
LoggerInstance.log('[publish] setMetadata result', res)
|
||||
|
||||
const txHash = res.transactionHash
|
||||
|
||||
setFeedback((prevState) => ({
|
||||
...prevState,
|
||||
'3': {
|
||||
...prevState['3'],
|
||||
status: res ? 'success' : 'error',
|
||||
txHash
|
||||
txHash: res?.transactionHash
|
||||
}
|
||||
}))
|
||||
|
||||
setDid(_ddo.id)
|
||||
return { did: ddo.id }
|
||||
} catch (error) {
|
||||
LoggerInstance.error('[publish] error', error.message)
|
||||
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 : (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
@ -224,22 +280,20 @@ export default function PublishPage({
|
||||
await handleSubmit(values)
|
||||
}}
|
||||
>
|
||||
{({ values }) => {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title={<Title networkId={values.user.chainId} />}
|
||||
description={content.description}
|
||||
/>
|
||||
<Form className={styles.form} ref={scrollToRef}>
|
||||
<Navigation />
|
||||
<Steps feedback={feedback} />
|
||||
<Actions scrollToRef={scrollToRef} did={did} />
|
||||
</Form>
|
||||
{debug && <Debug />}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
{({ values }) => (
|
||||
<>
|
||||
<PageHeader
|
||||
title={<Title networkId={values.user.chainId} />}
|
||||
description={content.description}
|
||||
/>
|
||||
<Form className={styles.form} ref={scrollToRef}>
|
||||
<Navigation />
|
||||
<Steps feedback={feedback} />
|
||||
<Actions scrollToRef={scrollToRef} did={did} />
|
||||
</Form>
|
||||
{debug && <Debug />}
|
||||
</>
|
||||
)}
|
||||
</Formik>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user