commons/client/src/components/templates/Asset/AssetDetails.tsx

381 lines
13 KiB
TypeScript

import React, { PureComponent, ChangeEvent } from 'react'
import Moment from 'react-moment'
import { DDO, MetaData, Logger } from '@oceanprotocol/squid'
import Input from '../../atoms/Form/Input'
import Markdown from '../../atoms/Markdown'
import { User } from '../../../context'
import CategoryLink from '../../atoms/CategoryLink'
import styles from './AssetDetails.module.scss'
import AssetFilesDetails from './AssetFilesDetails'
import Button from '../../atoms/Button'
import Spinner from '../../atoms/Spinner'
import Report from './Report'
import Web3 from 'web3'
import { serviceUri, allowPricing } from '../../../config'
const { steps } = require('../../../data/form-publish.json') // eslint-disable-line
export const renderDatafilesLine = (files: any) =>
files.length === 1 ? (
<span>{files.length} data file</span>
) : (
<span>{files.length} data files</span>
)
interface AssetDetailsProps {
metadata: MetaData
ddo: DDO
}
interface AssetDetailsState {
isEditMode?: boolean
isLoading?: boolean
feedback?: string
dateCreated?: string
description?: string
copyrightHolder?: string
categories?: string[]
}
export default class AssetDetails extends PureComponent<
AssetDetailsProps,
AssetDetailsState
> {
public state = {
isEditMode: false,
isLoading: false,
feedback: '',
dateCreated: this.props.metadata.base.dateCreated,
description: this.props.metadata.base.description,
copyrightHolder: this.props.metadata.base.copyrightHolder,
categories: this.props.metadata.base.categories
}
private inputChange = (
event: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>
) => {
this.setState({
[event.currentTarget.name]: event.currentTarget.value
})
}
private inputToArrayChange = (
event: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>
) => {
this.setState({
[event.currentTarget.name]: [event.currentTarget.value]
})
}
private toggleEditMode = () => {
this.setState({ isEditMode: !this.state.isEditMode })
}
private cancelEditMode = () => {
// reset everything
this.setState({
isEditMode: false,
feedback: '',
dateCreated: this.props.metadata.base.dateCreated,
description: this.props.metadata.base.description,
copyrightHolder: this.props.metadata.base.copyrightHolder,
categories: this.props.metadata.base.categories
})
}
private fetch = async (method: string, body: any) => {
try {
const response = await fetch(
`${serviceUri}/api/v1/ddo/${this.props.ddo}`,
{
method,
body: JSON.stringify({ body }),
headers: { 'Content-Type': 'application/json' }
}
)
const json = await response.json()
return json
} catch (error) {
Logger.error(error)
return error
}
}
private updateAsset = async () => {
this.setState({ isLoading: true, feedback: 'Updating asset...' })
const { web3, account } = this.context
const { id } = this.props.ddo
const {
dateCreated,
description,
copyrightHolder,
categories
} = this.state
const signature = await web3.eth.personal.sign(
`You are updating ${id}`,
account
)
const body = {
metadata: {
base: {
dateCreated,
description,
copyrightHolder,
categories
}
},
signature
}
const response = await this.fetch('PUT', body)
if (response.status !== 'success') {
this.setState({ feedback: response.message, isLoading: false })
} else {
this.setState({ isEditMode: false, isLoading: false })
}
}
private retireAsset = async () => {
this.setState({ isLoading: true, feedback: 'Retiring asset...' })
const { web3, account } = this.context
const { id } = this.props.ddo
const signature = await web3.eth.personal.sign(
`You are retiring ${id}`,
account
)
const body = { signature }
const response = await this.fetch('DELETE', body)
if (response.status !== 'success') {
this.setState({ feedback: response.message })
}
this.setState({ isLoading: false })
}
private CopyrightHolder = ({ value }: { value: string }) =>
this.state.isEditMode ? (
<Input
name={'copyrightHolder'}
label={steps[2].fields.copyrightHolder.label}
placeholder={steps[2].fields.copyrightHolder.placeholder}
required={steps[2].fields.copyrightHolder.required}
type={steps[2].fields.copyrightHolder.type}
onChange={this.inputChange}
value={value}
disabled={this.state.isLoading}
small
/>
) : (
<h2 className={styles.copyrightHolder} title="Copyright Holder">
{value}
</h2>
)
private Date = ({ value }: { value: string }) =>
this.state.isEditMode ? (
<Input
name={'dateCreated'}
label={steps[1].fields.dateCreated.label}
placeholder={steps[1].fields.dateCreated.placeholder}
required={steps[1].fields.dateCreated.required}
type={steps[1].fields.dateCreated.type}
onChange={this.inputChange}
value={value}
disabled={this.state.isLoading}
small
/>
) : (
<Moment date={value} format="L" interval={0} />
)
private Category = ({ value }: { value: string[] }) =>
this.state.isEditMode ? (
<Input
name={'categories'}
label={steps[1].fields.categories.label}
placeholder={steps[1].fields.categories.placeholder}
required={steps[1].fields.categories.required}
type={steps[1].fields.categories.type}
onChange={this.inputToArrayChange}
options={steps[1].fields.categories.options}
value={value[0]}
disabled={this.state.isLoading}
small
/>
) : (
// TODO: Make this link to search for respective category
<CategoryLink category={value[0]} />
)
private Description = ({ value }: { value: string }) =>
this.state.isEditMode ? (
<Input
name={'description'}
label={steps[1].fields.description.label}
placeholder={steps[1].fields.description.placeholder}
required={steps[1].fields.description.required}
type={steps[1].fields.description.type}
onChange={this.inputChange}
rows={10}
value={value}
disabled={this.state.isLoading}
/>
) : (
<Markdown text={value} className={styles.description} />
)
private MetadataActions = () => {
const isOwner = this.context.account === this.props.ddo.proof.creator
if (!isOwner) {
return null
}
return (
<div className={styles.metadataActions}>
{this.state.isEditMode ? (
<div className={styles.metadataActionEdit}>
{this.state.isLoading ? (
<Spinner message={this.state.feedback} />
) : (
<>
<Button primary onClick={this.updateAsset}>
Save Changes
</Button>
<Button link onClick={this.cancelEditMode}>
Cancel
</Button>
{this.state.feedback !== '' && (
<div>{this.state.feedback}</div>
)}
</>
)}
</div>
) : (
<>
<div>You are the owner of this asset</div>
<div>
<Button link onClick={this.toggleEditMode}>
Edit Metadata
</Button>
{/* TODO: Retire action needs loading/feedback */}
<Button link onClick={this.retireAsset}>
Retire Asset
</Button>
</div>
</>
)}
</div>
)
}
public render() {
const { metadata, ddo } = this.props
const { base } = metadata
const {
isEditMode,
copyrightHolder,
dateCreated,
categories,
description
} = this.state
return (
<>
<aside className={styles.metaPrimary}>
{copyrightHolder && (
<this.CopyrightHolder value={copyrightHolder} />
)}
<div className={styles.metaPrimaryData}>
<span
title={`Date created, published on ${base.datePublished}`}
>
<this.Date value={dateCreated} />
</span>
{categories && <this.Category value={categories} />}
{/* base.categories && (
<CategoryLink category={base.categories[0]} />
) */}
{base.files &&
!isEditMode &&
renderDatafilesLine(base.files)}
</div>
</aside>
{description && <this.Description value={description} />}
<this.MetadataActions />
<Report did={ddo.id} title={metadata.base.name} />
<div className={styles.metaFixed}>
<h2
className={styles.metaFixedTitle}
title="This metadata can not be changed because it is used to generate the checksums for the DDO, and to encrypt the file urls."
>
Fixed Metadata
</h2>
<ul>
<li>
<span className={styles.metaLabel}>
<strong>Author</strong>
</span>
<span className={styles.metaValue}>
{base.author}
</span>
</li>
<li>
<span className={styles.metaLabel}>
<strong>License</strong>
</span>
<span className={styles.metaValue}>
{base.license}
</span>
</li>
<li>
<span className={styles.metaLabel}>
<strong>DID</strong>
</span>
<span className={styles.metaValue}>
<code>{ddo.id}</code>
</span>
</li>
{allowPricing ? (
<li>
<span className={styles.metaLabel}>
<strong>Price</strong>
</span>
<span className={styles.metaValue}>
{base.price === '0'
? 0
: Web3.utils.fromWei(
base.price.toString()
)}{' '}
OCEAN
</span>
</li>
) : null}
</ul>
</div>
<AssetFilesDetails
files={base.files ? base.files : []}
ddo={ddo}
/>
</>
)
}
}
AssetDetails.contextType = User