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

Help messages & price display for starting compute job (#478)

* add dataset, algo and total price to form footer

* styled price component on start compute

* fix lint issues

* added help messages for compute jobs in BuyButton

* consume button text alignment fix

* help text copy changes

* styles cleanup

* use graph price on dataset display, remove buy compute timeout text

* minimal total price output

* spacing, copy

* more spacing & copy

* calculation output, timeout output

* price output refactor

Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
Bogdan Fazakas 2021-04-08 18:10:51 +03:00 committed by GitHub
parent ffca6f1bd6
commit 1d13000772
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 306 additions and 71 deletions

View File

@ -10,33 +10,66 @@ interface ButtonBuyProps {
hasDatatoken: boolean
dtSymbol: string
dtBalance: string
isLoading: boolean
assetType: string
assetTimeout: string
hasPreviousOrderSelectedComputeAsset?: boolean
hasDatatokenSelectedComputeAsset?: boolean
dtSymbolSelectedComputeAsset?: string
dtBalanceSelectedComputeAsset?: string
selectedComputeAssetType?: string
isLoading: boolean
onClick?: (e: FormEvent<HTMLButtonElement>) => void
stepText?: string
type?: 'submit'
}
function getHelpText(
token: {
dtBalance: string
dtSymbol: string
},
function getConsumeHelpText(
dtBalance: string,
dtSymbol: string,
hasDatatoken: boolean,
hasPreviousOrder: boolean,
timeout: string
assetType: string
) {
const { dtBalance, dtSymbol } = token
const assetTimeout = timeout === 'Forever' ? '' : ` for ${timeout}`
const text = hasPreviousOrder
? `You bought this data set already allowing you to use it without paying again${assetTimeout}.`
? `You bought this ${assetType} already allowing you to use it without paying again.`
: hasDatatoken
? `You own ${dtBalance} ${dtSymbol} allowing you to use this data set by spending 1 ${dtSymbol}, but without paying OCEAN again.`
: `For using this data set, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher and pool.`
: `For using this ${assetType}, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher and pool.`
return text
}
function getComputeAssetHelpText(
hasPreviousOrder: boolean,
hasDatatoken: boolean,
dtSymbol: string,
dtBalance: string,
assetType: string,
hasPreviousOrderSelectedComputeAsset?: boolean,
hasDatatokenSelectedComputeAsset?: boolean,
dtSymbolSelectedComputeAsset?: string,
dtBalanceSelectedComputeAsset?: string,
selectedComputeAssetType?: string
) {
const computeAssetHelpText = getConsumeHelpText(
dtBalance,
dtSymbol,
hasDatatoken,
hasPreviousOrder,
assetType
)
const text =
!dtSymbolSelectedComputeAsset && !dtBalanceSelectedComputeAsset
? ''
: hasPreviousOrderSelectedComputeAsset
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
: hasDatatokenSelectedComputeAsset
? `You own ${dtBalanceSelectedComputeAsset} ${dtSymbolSelectedComputeAsset} allowing you to use the selected ${selectedComputeAssetType} by spending 1 ${dtSymbolSelectedComputeAsset}, but without paying OCEAN again.`
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher and pool.`
return `${computeAssetHelpText} ${text}`
}
export default function ButtonBuy({
action,
disabled,
@ -44,8 +77,14 @@ export default function ButtonBuy({
hasDatatoken,
dtSymbol,
dtBalance,
onClick,
assetType,
assetTimeout,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
dtSymbolSelectedComputeAsset,
dtBalanceSelectedComputeAsset,
selectedComputeAssetType,
onClick,
stepText,
isLoading,
type
@ -55,11 +94,9 @@ export default function ButtonBuy({
? hasPreviousOrder
? 'Download'
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
: hasPreviousOrder
: hasPreviousOrder && hasPreviousOrderSelectedComputeAsset
? 'Start Compute Job'
: `Buy Compute Job ${
assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`
}`
: `Buy Compute Job`
return (
<div className={styles.actions}>
@ -76,12 +113,26 @@ export default function ButtonBuy({
{buttonText}
</Button>
<div className={styles.help}>
{getHelpText(
{ dtBalance, dtSymbol },
hasDatatoken,
hasPreviousOrder,
assetTimeout
)}
{action === 'download'
? getConsumeHelpText(
dtBalance,
dtSymbol,
hasDatatoken,
hasPreviousOrder,
assetType
)
: getComputeAssetHelpText(
hasPreviousOrder,
hasDatatoken,
dtSymbol,
dtBalance,
assetType,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
dtSymbolSelectedComputeAsset,
dtBalanceSelectedComputeAsset,
selectedComputeAssetType
)}
</div>
</>
)}

View File

@ -92,10 +92,14 @@ div [class*='loaderWrap'] {
.price {
white-space: pre;
font-size: var(--font-size-small) !important;
font-size: calc(var(--font-size-small) / 1.1) !important;
padding-left: calc(var(--spacer) / 4);
}
.price [class*='symbol'] {
font-size: calc(var(--font-size-small) / 1.2) !important;
}
.search {
margin: calc(var(--spacer) / 4) calc(var(--spacer) / 2);
width: calc(100% - var(--spacer));

View File

@ -1,11 +1,10 @@
.form {
padding: 0;
border: none;
margin-top: -0.8rem;
}
.form > div > label,
[class*='ButtonBuy-module--actions'] {
.form [class*='ButtonBuy-module--actions'] {
text-align: center;
}
@ -23,15 +22,3 @@
border-right: 0;
padding: 0;
}
.actions {
display: flex;
justify-content: center;
max-height: 100%;
}
.actions a,
.actions button {
margin-left: calc(var(--spacer) / 2);
margin-right: calc(var(--spacer) / 2);
}

View File

@ -1,12 +1,14 @@
import React, { ReactElement, useEffect } from 'react'
import React, { ReactElement, useEffect, useState } from 'react'
import styles from './FormComputeDataset.module.css'
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
import Input from '../../../atoms/Input'
import { FormFieldProps } from '../../../../@types/Form'
import { useStaticQuery, graphql } from 'gatsby'
import { DDO } from '@oceanprotocol/lib'
import { DDO, BestPrice } from '@oceanprotocol/lib'
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
import ButtonBuy from '../../../atoms/ButtonBuy'
import PriceOutput from './PriceOutput'
import { useAsset } from '../../../../providers/Asset'
const contentQuery = graphql`
query StartComputeDatasetQuery {
@ -46,10 +48,17 @@ export default function FormStartCompute({
isComputeButtonDisabled,
hasPreviousOrder,
hasDatatoken,
dtSymbol,
dtBalance,
assetType,
assetTimeout,
hasPreviousOrderSelectedComputeAsset,
hasDatatokenSelectedComputeAsset,
dtSymbolSelectedComputeAsset,
dtBalanceSelectedComputeAsset,
selectedComputeAssetType,
selectedComputeAssetTimeout,
stepText,
datasetTimeout
algorithmPrice
}: {
algorithms: AssetSelectionAsset[]
ddoListAlgorithms: DDO[]
@ -58,10 +67,17 @@ export default function FormStartCompute({
isComputeButtonDisabled: boolean
hasPreviousOrder: boolean
hasDatatoken: boolean
dtSymbol: string
dtBalance: string
assetType: string
assetTimeout: string
hasPreviousOrderSelectedComputeAsset?: boolean
hasDatatokenSelectedComputeAsset?: boolean
dtSymbolSelectedComputeAsset?: string
dtBalanceSelectedComputeAsset?: string
selectedComputeAssetType?: string
selectedComputeAssetTimeout?: string
stepText: string
datasetTimeout: string
algorithmPrice: BestPrice
}): ReactElement {
const data = useStaticQuery(contentQuery)
const content = data.content.edges[0].node.childPagesJson
@ -70,6 +86,8 @@ export default function FormStartCompute({
isValid,
values
}: FormikContextType<{ algorithm: string }> = useFormikContext()
const { price, ddo } = useAsset()
const [totalPrice, setTotalPrice] = useState(price?.value)
function getAlgorithmAsset(algorithmId: string): DDO {
let assetDdo = null
@ -84,6 +102,25 @@ export default function FormStartCompute({
setSelectedAlgorithm(getAlgorithmAsset(values.algorithm))
}, [values.algorithm])
//
// Set price for calculation output
//
useEffect(() => {
if (!price || !algorithmPrice) return
const priceDataset = hasPreviousOrder ? 0 : Number(price.value)
const priceAlgo = hasPreviousOrderSelectedComputeAsset
? 0
: Number(algorithmPrice.value)
setTotalPrice(priceDataset + priceAlgo)
}, [
price,
algorithmPrice,
hasPreviousOrder,
hasPreviousOrderSelectedComputeAsset
])
return (
<Form className={styles.form}>
{content.form.data.map((field: FormFieldProps) => (
@ -95,17 +132,36 @@ export default function FormStartCompute({
/>
))}
<PriceOutput
hasPreviousOrder={hasPreviousOrder}
assetTimeout={assetTimeout}
hasPreviousOrderSelectedComputeAsset={
hasPreviousOrderSelectedComputeAsset
}
selectedComputeAssetTimeout={selectedComputeAssetTimeout}
algorithmPrice={algorithmPrice}
totalPrice={totalPrice}
/>
<ButtonBuy
action="compute"
disabled={isComputeButtonDisabled || !isValid}
hasPreviousOrder={hasPreviousOrder}
hasDatatoken={hasDatatoken}
dtSymbol={dtSymbol}
dtSymbol={ddo.dataTokenInfo.symbol}
dtBalance={dtBalance}
assetTimeout={assetTimeout}
assetType={assetType}
hasPreviousOrderSelectedComputeAsset={
hasPreviousOrderSelectedComputeAsset
}
hasDatatokenSelectedComputeAsset={hasDatatokenSelectedComputeAsset}
dtSymbolSelectedComputeAsset={dtSymbolSelectedComputeAsset}
dtBalanceSelectedComputeAsset={dtBalanceSelectedComputeAsset}
selectedComputeAssetType={selectedComputeAssetType}
stepText={stepText}
isLoading={isLoading}
type="submit"
assetTimeout={datasetTimeout}
/>
</Form>
)

View File

@ -0,0 +1,54 @@
.priceComponent {
margin-left: -2rem;
margin-right: -2rem;
margin-top: -1rem;
margin-bottom: calc(var(--spacer) / 1.5);
padding-left: calc(var(--spacer) / 2);
padding-right: calc(var(--spacer) / 2);
border-bottom: 1px solid var(--border-color);
padding-bottom: calc(var(--spacer) / 3);
text-align: center;
color: var(--color-secondary);
font-size: var(--font-size-small);
}
.priceComponent > * {
display: inline-block !important;
}
.calculation {
min-width: 12rem;
}
.timeout {
display: block;
text-align: right;
font-size: var(--font-size-mini);
color: var(--color-secondary);
}
.calculation .price {
font-size: var(--font-size-small) !important;
}
.priceRow {
width: 100%;
border-bottom: 1px solid var(--border-color);
padding-top: calc(var(--spacer) / 7);
padding-bottom: calc(var(--spacer) / 7);
display: flex;
justify-content: space-between;
}
.priceRow:last-child {
border-bottom: none;
border-top: 1px solid var(--border-color);
}
.sign {
display: inline-block;
width: 5%;
text-align: left;
color: var(--color-secondary);
font-size: var(--font-size-base);
}

View File

@ -0,0 +1,81 @@
import { BestPrice } from '@oceanprotocol/lib'
import React, { ReactElement } from 'react'
import { useAsset } from '../../../../providers/Asset'
import PriceUnit from '../../../atoms/Price/PriceUnit'
import Tooltip from '../../../atoms/Tooltip'
import styles from './PriceOutput.module.css'
interface PriceOutputProps {
totalPrice: number
hasPreviousOrder: boolean
assetTimeout: string
hasPreviousOrderSelectedComputeAsset: boolean
algorithmPrice: BestPrice
selectedComputeAssetTimeout: string
}
function Row({
price,
hasPreviousOrder,
timeout,
sign
}: {
price: number
hasPreviousOrder?: boolean
timeout?: string
sign?: string
}) {
return (
<div className={styles.priceRow}>
<div className={styles.sign}>{sign}</div>
<div>
<PriceUnit
price={hasPreviousOrder ? '0' : `${price}`}
small
className={styles.price}
/>
<span className={styles.timeout}>
{timeout &&
timeout !== 'Forever' &&
!hasPreviousOrder &&
`for ${timeout}`}
</span>
</div>
</div>
)
}
export default function PriceOutput({
totalPrice,
hasPreviousOrder,
assetTimeout,
hasPreviousOrderSelectedComputeAsset,
algorithmPrice,
selectedComputeAssetTimeout
}: PriceOutputProps): ReactElement {
const { price } = useAsset()
return (
<div className={styles.priceComponent}>
You will pay <PriceUnit price={`${totalPrice}`} small />
<Tooltip
content={
<div className={styles.calculation}>
<Row
hasPreviousOrder={hasPreviousOrder}
price={price?.value}
timeout={assetTimeout}
/>
<Row
hasPreviousOrder={hasPreviousOrderSelectedComputeAsset}
price={algorithmPrice?.value}
timeout={selectedComputeAssetTimeout}
sign="+"
/>
<Row price={totalPrice} sign="=" />
</div>
}
/>
</div>
)
}

View File

@ -2,21 +2,13 @@
display: flex;
align-items: center;
width: auto;
margin-bottom: var(--spacer);
margin-bottom: calc(var(--spacer) / 2);
border-bottom: 1px solid var(--border-color);
margin-top: -1rem;
margin-left: -2rem;
margin-right: -2rem;
padding: 0 var(--spacer) calc(var(--spacer) / 2) var(--spacer);
}
.filewrapper {
flex-shrink: 0;
}
.actions {
margin-top: var(--spacer);
text-align: center;
padding: 0 calc(var(--spacer) / 2) calc(var(--spacer) / 2)
calc(var(--spacer) * 1.5);
}
.feedback {
@ -24,6 +16,6 @@
margin-top: calc(var(--spacer) / 2);
}
.help {
composes: help from '../index.module.css';
.feedback:empty {
margin-top: 0;
}

View File

@ -1,7 +1,6 @@
import React, { ReactElement } from 'react'
import Compute from '.'
import ddo from '../../../../../tests/unit/__fixtures__/ddo'
import { DDO } from '@oceanprotocol/lib'
export default {
title: 'Organisms/Compute',
@ -14,7 +13,6 @@ export default {
export const Default = (): ReactElement => (
<Compute
ddo={ddo as DDO}
dtBalance="1"
isBalanceSufficient
file={ddo.service[0].attributes.main.files[0]}

View File

@ -62,12 +62,10 @@ const poolQuery = gql`
`
export default function Compute({
ddo,
isBalanceSufficient,
dtBalance,
file
}: {
ddo: DDO
isBalanceSufficient: boolean
dtBalance: string
file: FileMetadata
@ -75,7 +73,7 @@ export default function Compute({
const { marketFeeAddress } = useSiteMetadata()
const { accountId } = useWeb3()
const { ocean, account, config } = useOcean()
const { price, type } = useAsset()
const { price, type, ddo } = useAsset()
const { buyDT, pricingError, pricingStepText } = usePricing()
const [isJobStarting, setIsJobStarting] = useState(false)
const [error, setError] = useState<string>()
@ -90,6 +88,7 @@ export default function Compute({
const [hasPreviousAlgorithmOrder, setHasPreviousAlgorithmOrder] = useState(
false
)
const [algorithmDTBalance, setalgorithmDTBalance] = useState<string>()
const [algorithmPrice, setAlgorithmPrice] = useState<BestPrice>()
const [variables, setVariables] = useState({})
const [
@ -97,6 +96,7 @@ export default function Compute({
setPreviousAlgorithmOrderId
] = useState<string>()
const [datasetTimeout, setDatasetTimeout] = useState<string>()
const [algorithmTimeout, setAlgorithmTimeout] = useState<string>()
/* eslint-disable @typescript-eslint/no-unused-vars */
const {
@ -138,6 +138,7 @@ export default function Compute({
asset.dataToken,
accountId
)
setalgorithmDTBalance(AssetDtBalance)
setHasAlgoAssetDatatoken(Number(AssetDtBalance) >= 1)
}
@ -264,6 +265,10 @@ export default function Compute({
}
checkAssetDTBalance(selectedAlgorithmAsset)
initMetadata(selectedAlgorithmAsset)
const { timeout } = (
ddo.findServiceByType('access') || ddo.findServiceByType('compute')
).attributes.main
setAlgorithmTimeout(secondsToString(timeout))
}, [selectedAlgorithmAsset, ocean, accountId, hasPreviousAlgorithmOrder])
// Output errors in toast UI
@ -415,7 +420,7 @@ export default function Compute({
<>
<div className={styles.info}>
<File file={file} small />
<Price price={(ddo as DDO).price} conversion />
<Price price={price} conversion />
</div>
{type === 'algorithm' ? (
@ -436,14 +441,21 @@ export default function Compute({
setSelectedAlgorithm={setSelectedAlgorithmAsset}
isLoading={isJobStarting}
isComputeButtonDisabled={isComputeButtonDisabled}
hasPreviousOrder={
hasPreviousDatasetOrder || hasPreviousAlgorithmOrder
}
hasPreviousOrder={hasPreviousDatasetOrder}
hasDatatoken={hasDatatoken}
dtSymbol={ddo.dataTokenInfo?.symbol}
dtBalance={dtBalance}
assetType={type}
assetTimeout={datasetTimeout}
hasPreviousOrderSelectedComputeAsset={hasPreviousAlgorithmOrder}
hasDatatokenSelectedComputeAsset={hasAlgoAssetDatatoken}
dtSymbolSelectedComputeAsset={
selectedAlgorithmAsset?.dataTokenInfo?.symbol
}
dtBalanceSelectedComputeAsset={algorithmDTBalance}
selectedComputeAssetType="algorithm"
selectedComputeAssetTimeout={algorithmTimeout}
stepText={pricingStepText || 'Starting Compute Job...'}
datasetTimeout={datasetTimeout}
algorithmPrice={algorithmPrice}
/>
</Formik>
)}

View File

@ -47,7 +47,7 @@ export default function Consume({
const { marketFeeAddress } = useSiteMetadata()
const [hasPreviousOrder, setHasPreviousOrder] = useState(false)
const [previousOrderId, setPreviousOrderId] = useState<string>()
const { isInPurgatory, price } = useAsset()
const { isInPurgatory, price, type } = useAsset()
const {
buyDT,
pricingStepText,
@ -153,6 +153,7 @@ export default function Consume({
dtBalance={dtBalance}
onClick={handleConsume}
assetTimeout={assetTimeout}
assetType={type}
stepText={consumeStepText || pricingStepText}
isLoading={pricingIsLoading}
/>

View File

@ -54,7 +54,6 @@ export default function AssetActions(): ReactElement {
const UseContent = isCompute ? (
<Compute
ddo={ddo}
dtBalance={dtBalance}
isBalanceSufficient={isBalanceSufficient}
file={metadata?.main.files[0]}