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

Merge pull request #52 from oceanprotocol/feature/price-refactor

refactor price component
This commit is contained in:
Matthias Kretschmann 2020-08-11 10:33:12 +02:00 committed by GitHub
commit 65b7b69aa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 110 deletions

View File

@ -34,6 +34,7 @@
"axios": "^0.19.2", "axios": "^0.19.2",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"date-fns": "^2.15.0", "date-fns": "^2.15.0",
"decimal.js": "^10.2.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"ethereum-blockies": "github:MyEtherWallet/blockies", "ethereum-blockies": "github:MyEtherWallet/blockies",
"filesize": "^6.1.0", "filesize": "^6.1.0",

View File

@ -2,6 +2,7 @@
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
font-size: var(--font-size-large); font-size: var(--font-size-large);
color: var(--brand-grey-dark); color: var(--brand-grey-dark);
line-height: 1;
} }
.price span:first-child { .price span:first-child {
@ -10,6 +11,11 @@
font-size: var(--font-size-base); font-size: var(--font-size-base);
} }
.empty {
color: var(--color-secondary);
font-weight: var(--font-weight-bold);
}
.small { .small {
/* lazy making-conversion-smaller-with-same-markup */ /* lazy making-conversion-smaller-with-same-markup */
transform: scale(0.8); transform: scale(0.8);

View File

@ -1,20 +1,39 @@
import React, { ReactElement } from 'react' import React, { ReactElement, useState, useEffect } from 'react'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import PriceConversion from './Conversion' import PriceConversion from './Conversion'
import styles from './index.module.css' import styles from './index.module.css'
import { formatCurrency } from '@coingecko/cryptoformat' import { formatCurrency } from '@coingecko/cryptoformat'
import { useMetadata, useOcean } from '@oceanprotocol/react'
import { DDO } from '@oceanprotocol/lib'
import Loader from '../Loader'
import Tooltip from '../Tooltip'
const cx = classNames.bind(styles) const cx = classNames.bind(styles)
export default function Price({ export default function Price({
price, ddo,
className, className,
small small,
setPriceOutside
}: { }: {
price: string // expects price in OCEAN, not wei ddo: DDO
className?: string className?: string
small?: boolean small?: boolean
setPriceOutside?: (price: string) => void
}): ReactElement { }): ReactElement {
const { chainId } = useOcean()
const { getBestPrice } = useMetadata()
const [price, setPrice] = useState<string>()
useEffect(() => {
async function init() {
const price = await getBestPrice(ddo.dataToken)
setPrice(price)
setPriceOutside && price !== '' && setPriceOutside(price)
}
init()
}, [chainId])
const styleClasses = cx({ const styleClasses = cx({
price: true, price: true,
small: small, small: small,
@ -32,5 +51,14 @@ export default function Price({
</> </>
) )
return <div className={styleClasses}>{displayPrice}</div> return price ? (
<div className={styleClasses}>{displayPrice}</div>
) : price === '' ? (
<div className={styles.empty}>
No price found{' '}
<Tooltip content="We could not find a pool for this data set, which can have multiple reasons. Is your wallet connected to the correct network?" />
</div>
) : (
<Loader message="Retrieving price..." />
)
} }

View File

@ -23,17 +23,6 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
const { description } = metadata.additionalInformation const { description } = metadata.additionalInformation
const isCompute = Boolean(ddo.findServiceByType('compute')) const isCompute = Boolean(ddo.findServiceByType('compute'))
const { getBestPrice } = useMetadata(ddo.id)
const [price, setPrice] = useState<string>()
useEffect(() => {
async function init() {
const price = await getBestPrice(ddo.dataToken)
setPrice(price)
}
init()
}, [])
return ( return (
<article className={styles.teaser}> <article className={styles.teaser}>
<Link to={`/asset/${ddo.id}`} className={styles.link}> <Link to={`/asset/${ddo.id}`} className={styles.link}>
@ -47,13 +36,7 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
</div> </div>
<footer className={styles.foot}> <footer className={styles.foot}>
{price ? ( <Price ddo={ddo} small />
<Price price={price} small />
) : price === '' ? (
'No price found'
) : (
<Loader message="Retrieving price..." />
)}
</footer> </footer>
</Link> </Link>
</article> </article>

View File

@ -12,15 +12,20 @@ export declare type Web3Error = {
} }
export default function Web3Feedback({ export default function Web3Feedback({
isBalanceInsufficient isBalanceSufficient
}: { }: {
isBalanceInsufficient?: boolean isBalanceSufficient?: boolean
}): ReactElement { }): ReactElement {
const { appConfig } = useSiteMetadata() const { appConfig } = useSiteMetadata()
const { account, status, chainId } = useOcean() const { account, status, chainId } = useOcean()
const isOceanConnectionError = status === -1 const isOceanConnectionError = status === -1
const correctNetwork = isCorrectNetwork(chainId) const correctNetwork = isCorrectNetwork(chainId)
const showFeedback = !account || isOceanConnectionError || !correctNetwork const showFeedback =
!account ||
isOceanConnectionError ||
!correctNetwork ||
isBalanceSufficient === false
const desiredNetworkName = appConfig.network.replace(/^\w/, (c: string) => const desiredNetworkName = appConfig.network.replace(/^\w/, (c: string) =>
c.toUpperCase() c.toUpperCase()
) )
@ -29,7 +34,7 @@ export default function Web3Feedback({
? 'error' ? 'error'
: !correctNetwork : !correctNetwork
? 'warning' ? 'warning'
: account && !isBalanceInsufficient : account && isBalanceSufficient
? 'success' ? 'success'
: 'warning' : 'warning'
@ -40,7 +45,7 @@ export default function Web3Feedback({
: !correctNetwork : !correctNetwork
? 'Wrong Network' ? 'Wrong Network'
: account : account
? isBalanceInsufficient === true ? isBalanceSufficient === false
? 'Insufficient balance' ? 'Insufficient balance'
: 'Connected to Ocean' : 'Connected to Ocean'
: 'Something went wrong' : 'Something went wrong'
@ -51,7 +56,7 @@ export default function Web3Feedback({
? 'Please try again.' ? 'Please try again.'
: !correctNetwork : !correctNetwork
? `Please connect to ${desiredNetworkName}.` ? `Please connect to ${desiredNetworkName}.`
: isBalanceInsufficient === true : isBalanceSufficient === false
? 'You do not have enough OCEAN in your wallet to purchase this asset.' ? 'You do not have enough OCEAN in your wallet to purchase this asset.'
: 'Something went wrong.' : 'Something went wrong.'

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect, ReactElement } from 'react' import React, { useState, ReactElement } from 'react'
import { DDO } from '@oceanprotocol/lib' import { DDO } from '@oceanprotocol/lib'
import compareAsBN, { Comparisson } from '../../../utils/compareAsBN'
import Loader from '../../atoms/Loader' import Loader from '../../atoms/Loader'
import Web3Feedback from '../../molecules/Wallet/Feedback' import Web3Feedback from '../../molecules/Wallet/Feedback'
import Dropzone from '../../atoms/Dropzone' import Dropzone from '../../atoms/Dropzone'
@ -18,10 +17,12 @@ import Alert from '../../atoms/Alert'
export default function Compute({ export default function Compute({
ddo, ddo,
price isBalanceSufficient,
setPrice
}: { }: {
ddo: DDO ddo: DDO
price: string // in OCEAN, not wei isBalanceSufficient: boolean
setPrice: (price: string) => void
}): ReactElement { }): ReactElement {
const { ocean } = useOcean() const { ocean } = useOcean()
const { compute, isLoading, computeStepText, computeError } = useCompute() const { compute, isLoading, computeStepText, computeError } = useCompute()
@ -29,7 +30,6 @@ export default function Compute({
const [isJobStarting, setIsJobStarting] = useState(false) const [isJobStarting, setIsJobStarting] = useState(false)
const [, setError] = useState('') const [, setError] = useState('')
const [isBalanceSufficient, setIsBalanceSufficient] = useState(false)
const [computeType, setComputeType] = useState('') const [computeType, setComputeType] = useState('')
const [computeContainer, setComputeContainer] = useState({ const [computeContainer, setComputeContainer] = useState({
entrypoint: '', entrypoint: '',
@ -39,25 +39,13 @@ export default function Compute({
const [algorithmRawCode, setAlgorithmRawCode] = useState('') const [algorithmRawCode, setAlgorithmRawCode] = useState('')
const [isPublished, setIsPublished] = useState(false) const [isPublished, setIsPublished] = useState(false)
const [file, setFile] = useState(null) const [file, setFile] = useState(null)
const [isTermsAgreed, setIsTermsAgreed] = useState(true)
const isFree = price === '0'
const isComputeButtonDisabled = const isComputeButtonDisabled =
isJobStarting || isJobStarting ||
file === null || file === null ||
computeType === '' || computeType === '' ||
!ocean || !ocean ||
!isBalanceSufficient || !isBalanceSufficient
!isTermsAgreed
// useEffect(() => {
// setIsBalanceSufficient(
// isFree ||
// (balance !== null &&
// compareAsBN(balance, fromWei(computeService.cost), Comparisson.gte))
// )
// }, [balance])
const onDrop = async (files: any) => { const onDrop = async (files: any) => {
setFile(files[0]) setFile(files[0])
@ -101,13 +89,7 @@ export default function Compute({
return ( return (
<div className={styles.compute}> <div className={styles.compute}>
{price ? ( <Price ddo={ddo} setPriceOutside={setPrice} />
<Price price={price} small />
) : price === '' ? (
'No price found'
) : (
<Loader message="Retrieving price..." />
)}
<div className={styles.info}> <div className={styles.info}>
<div className={styles.selectType}> <div className={styles.selectType}>
@ -133,7 +115,6 @@ export default function Compute({
Start job Start job
</Button> </Button>
</div> </div>
{/* <TermsCheckbox onChange={onCheck} /> */}
</div> </div>
<footer className={styles.feedback}> <footer className={styles.feedback}>
@ -148,7 +129,7 @@ export default function Compute({
state="success" state="success"
/> />
)} )}
<Web3Feedback isBalanceInsufficient={!isBalanceSufficient} /> <Web3Feedback isBalanceSufficient={isBalanceSufficient} />
</footer> </footer>
</div> </div>
</div> </div>

View File

@ -12,16 +12,18 @@
flex-shrink: 0; flex-shrink: 0;
} }
.consume button { .pricewrapper {
margin-left: calc(var(--spacer) / 4); display: flex;
flex-wrap: wrap;
} }
.consume button:first-of-type { .pricewrapper button {
margin-left: 0; margin-top: calc(var(--spacer) / 2);
align-self: flex-end;
} }
.price { .pricewrapper > div {
margin-bottom: calc(var(--spacer) / 2); min-height: 30px;
} }
.feedback { .feedback {

View File

@ -1,7 +1,6 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { File as FileMetadata, DDO } from '@oceanprotocol/lib' import { File as FileMetadata, DDO } from '@oceanprotocol/lib'
import compareAsBN, { Comparisson } from '../../../utils/compareAsBN'
import Button from '../../atoms/Button' import Button from '../../atoms/Button'
import File from '../../atoms/File' import File from '../../atoms/File'
import Price from '../../atoms/Price' import Price from '../../atoms/Price'
@ -12,21 +11,19 @@ import { useOcean, useConsume } from '@oceanprotocol/react'
export default function Consume({ export default function Consume({
ddo, ddo,
price, file,
file isBalanceSufficient,
setPrice
}: { }: {
ddo: DDO ddo: DDO
price: string // in OCEAN, not wei
file: FileMetadata file: FileMetadata
isBalanceSufficient: boolean
setPrice: (price: string) => void
}): ReactElement { }): ReactElement {
const accessService = ddo.findServiceByType('access')
const { ocean } = useOcean() const { ocean } = useOcean()
const { consumeStepText, consume, consumeError } = useConsume() const { consumeStepText, consume, consumeError } = useConsume()
const isFree = price === '0' const isDisabled = !ocean || !isBalanceSufficient
// const isBalanceSufficient =
// isFree || compareAsBN(balanceInOcean, fromWei(cost), Comparisson.gte)
const isDisabled = !ocean
if (consumeError) { if (consumeError) {
toast.error(consumeError) toast.error(consumeError)
@ -41,7 +38,7 @@ export default function Consume({
onClick={() => consume(ddo.id, ddo.dataToken, 'access')} onClick={() => consume(ddo.id, ddo.dataToken, 'access')}
disabled={isDisabled} disabled={isDisabled}
> >
{isFree ? 'Download' : 'Buy'} Buy
</Button> </Button>
) )
@ -52,19 +49,13 @@ export default function Consume({
<File file={file} /> <File file={file} />
</div> </div>
<div className={styles.pricewrapper}> <div className={styles.pricewrapper}>
{price ? ( <Price ddo={ddo} setPriceOutside={setPrice} />
<Price price={price} small />
) : price === '' ? (
'No price found'
) : (
<Loader message="Retrieving price..." />
)}
<PurchaseButton /> <PurchaseButton />
</div> </div>
</div> </div>
<footer className={styles.feedback}> <footer className={styles.feedback}>
<Web3Feedback isBalanceInsufficient /> <Web3Feedback isBalanceSufficient={isBalanceSufficient} />
</footer> </footer>
</aside> </aside>
) )

View File

@ -5,7 +5,8 @@ import Consume from './Consume'
import { MetadataMarket } from '../../../@types/Metadata' import { MetadataMarket } from '../../../@types/Metadata'
import { DDO } from '@oceanprotocol/lib' import { DDO } from '@oceanprotocol/lib'
import Tabs from '../../atoms/Tabs' import Tabs from '../../atoms/Tabs'
import { useMetadata } from '@oceanprotocol/react' import { useOcean } from '@oceanprotocol/react'
import compareAsBN from '../../../utils/compareAsBN'
export default function AssetActions({ export default function AssetActions({
metadata, metadata,
@ -14,22 +15,37 @@ export default function AssetActions({
metadata: MetadataMarket metadata: MetadataMarket
ddo: DDO ddo: DDO
}): ReactElement { }): ReactElement {
const { getBestPrice } = useMetadata(ddo.id) const { balance } = useOcean()
const [price, setPrice] = useState<string>() const [price, setPrice] = useState<string>()
const [isBalanceSufficient, setIsBalanceSufficient] = useState<boolean>()
useEffect(() => {
async function init() {
const price = await getBestPrice(ddo.dataToken)
setPrice(price)
}
init()
}, [])
const isCompute = Boolean(ddo.findServiceByType('compute')) const isCompute = Boolean(ddo.findServiceByType('compute'))
// Check user balance against price
useEffect(() => {
if (!price || !balance || !balance.ocean) return
const isFree = price === '0'
setIsBalanceSufficient(isFree ? true : compareAsBN(balance.ocean, price))
return () => {
setIsBalanceSufficient(false)
}
}, [balance, price])
const UseContent = isCompute ? ( const UseContent = isCompute ? (
<Compute ddo={ddo} price={price} /> <Compute
ddo={ddo}
isBalanceSufficient={isBalanceSufficient}
setPrice={setPrice}
/>
) : ( ) : (
<Consume ddo={ddo} price={price} file={metadata.main.files[0]} /> <Consume
ddo={ddo}
isBalanceSufficient={isBalanceSufficient}
file={metadata.main.files[0]}
setPrice={setPrice}
/>
) )
const tabs = [ const tabs = [

View File

@ -1,21 +1,16 @@
import BN from 'bn.js' import { Decimal } from 'decimal.js'
export enum Comparisson { // Run decimal.js comparison
'lt' = 'lt', // http://mikemcl.github.io/decimal.js/#cmp
'lte' = 'lte', export default function compareAsBN(balance: string, price: string): boolean {
'gt' = 'gt', const aBN = new Decimal(balance)
'gte' = 'gte', const bBN = new Decimal(price)
'eq' = 'eq' const compare = aBN.comparedTo(bBN)
}
// Run the corresponding bn.js comparisson: switch (compare) {
// https://github.com/indutny/bn.js/#utilities case 1 || 0: // balance is greater or equal to price
export default function compareAsBN( return true
a: string, default:
b: string, return false
comparisson: Comparisson }
) {
const aBN = new BN(a)
const bBN = new BN(b)
return aBN[comparisson](bBN)
} }