Skip to content

Commit

Permalink
feat(nfts): add offers step
Browse files Browse the repository at this point in the history
  • Loading branch information
plondon committed Dec 16, 2021
1 parent 349e64a commit 5ead33f
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { promptForSecondPassword } from 'services/sagas'

import * as S from './selectors'
import { actions as A } from './slice'
import { NftOrderStepEnum } from './types'
import { orderFromJSON } from './utils'

export const logLocation = 'components/nfts/sagas'
Expand Down Expand Up @@ -519,11 +520,27 @@ export default ({ api }: { api: APIType }) => {
token_id = asset!.tokenId
}
// User wants to sell an asset
else if (action.payload.asset) {
if (action.payload.asset) {
address = action.payload.asset.asset_contract.address
token_id = action.payload.asset.token_id
}

if (action.payload.offer) {
// User wants to cancel offer
if (action.payload.offer.from_account.address.toLowerCase() === ethAddr.toLowerCase()) {
debugger
// const activeOrder = yield call(
// api.getOrderByContractAndId,
// action.payload.offer.asset.asset_contract.address,
// action.payload.offer.asset.token_id
// )
yield put(A.setOrderFlowStep({ step: NftOrderStepEnum.CANCEL_OFFER }))
} else {
// User wants to accept offer
yield put(A.setOrderFlowStep({ step: NftOrderStepEnum.ACCEPT_OFFER }))
}
}

try {
yield put(actions.components.nfts.fetchNftOrderAssetLoading())
const asset = yield call(api.getNftAsset, address, token_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ const nftsSlice = createSlice({
nftOrderFlowOpen: (
state,
action: PayloadAction<
{ asset: NftAsset; order?: never } | { asset?: never; order: NftOrdersType['orders'][0] }
| { asset: NftAsset; offer: OfferEventsType['asset_events'][0]; order?: never }
| { asset: NftAsset; offer?: never; order?: never }
| { asset?: never; offer?: never; order: NftOrdersType['orders'][0] }
>
) => {
if (action.payload.order) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { calculateGasFees } from '@core/redux/payment/nfts'
import { Await, RemoteDataType } from '@core/types'

export enum NftOrderStepEnum {
ACCEPT_OFFER = 'ACCEPT_OFFER',
CANCEL_OFFER = 'CANCEL_OFFER',
CONFIRM_BUY = 'CONFIRM_BUY',
MAKE_OFFER = 'MAKE_OFFER',
MARK_FOR_SALE = 'MARK_FOR_SALE',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { connect, ConnectedProps } from 'react-redux'
import { compose } from 'redux'
import { Field, reduxForm } from 'redux-form'

import { Remote } from '@core'
import { convertCoinToCoin } from '@core/exchange'
import { Button, Icon, SpinningLoader, Text } from 'blockchain-info-components'
import CoinDisplay from 'components/Display/CoinDisplay'
import FiatDisplay from 'components/Display/FiatDisplay'
import { Row, Title, Value } from 'components/Flyout/model'
import { Form, NumberBox, SelectBox } from 'components/Form'
import { selectors } from 'data'
import { NftOrderStepEnum } from 'data/components/nfts/types'

import { AssetDesc, FullAssetImage, StickyCTA } from '../../components'
import { Props as OwnProps } from '..'
// import CancelOfferFees from './fees'

const CancelOffer: React.FC<Props> = (props) => {
const { close, formValues, nftActions, orderFlow } = props
const { activeOrder } = orderFlow

const disabled =
!formValues.amount || Remote.Loading.is(orderFlow.order) || Remote.Loading.is(orderFlow.fees)

return (
<>
{orderFlow.asset.cata({
Failure: (e) => <Text>{e}</Text>,
Loading: () => (
<AssetDesc>
<SpinningLoader width='14px' height='14px' borderWidth='3px' />
</AssetDesc>
),
NotAsked: () => null,
Success: (val) => (
<>
<div style={{ position: 'relative' }}>
<Icon
onClick={() => nftActions.setOrderFlowStep({ step: NftOrderStepEnum.SHOW_ASSET })}
name='arrow-left'
cursor
role='button'
style={{ left: '40px', position: 'absolute', top: '40px' }}
/>
<Icon
onClick={() => close()}
name='close'
cursor
role='button'
style={{ position: 'absolute', right: '40px', top: '40px' }}
/>
<FullAssetImage cropped backgroundImage={val?.image_url.replace(/=s\d*/, '')} />
</div>
<AssetDesc>
<Text size='16px' color='grey900' weight={600}>
{val?.collection?.name}
</Text>
<Text style={{ marginTop: '4px' }} size='20px' color='grey900' weight={600}>
{val?.name}
</Text>
</AssetDesc>
<Row>
<Title>
<FormattedMessage id='copy.description' defaultMessage='Description' />
</Title>
<Value>
{val?.description || (
<FormattedMessage id='copy.none_found' defaultMessage='None found.' />
)}
</Value>
</Row>
<Form>
<Row>
<Title>
<b>
<FormattedMessage id='copy.select_coin' defaultMessage='Select Coin' />
</b>
</Title>
<Value>
<Field
name='coin'
component={SelectBox}
elements={[
{
group: '',
items: val.collection.payment_tokens
.map((token) => token.symbol)
.filter((symbol) => symbol === 'WETH')
.map((coin) => ({
text: window.coins[coin].coinfig.symbol,
value: window.coins[coin].coinfig.symbol
}))
}
]}
/>
</Value>
</Row>
<>
<Row>
<Title>
<b>
<FormattedMessage id='copy.amount' defaultMessage='Amount' />
</b>
</Title>
<Value>
<Field name='amount' component={NumberBox} />
</Value>
<Value>
<FiatDisplay size='12px' weight={600} coin={formValues.coin}>
{convertCoinToCoin({
baseToStandard: false,
coin: formValues.coin,
value: formValues.amount
}) || 0}
</FiatDisplay>
</Value>
</Row>
{activeOrder ? (
<Row>
<Title>
<FormattedMessage id='copy.current_price' defaultMessage='Current Price' />
</Title>
<Value>
<div style={{ display: 'flex' }}>
<CoinDisplay
size='14px'
color='black'
weight={600}
coin={activeOrder.paymentTokenContract?.symbol}
>
{activeOrder.basePrice}
</CoinDisplay>
&nbsp;-&nbsp;
<FiatDisplay
size='12px'
color='grey600'
weight={600}
coin={activeOrder.paymentTokenContract?.symbol}
>
{activeOrder.basePrice}
</FiatDisplay>
</div>
</Value>
</Row>
) : null}
</>
</Form>
{activeOrder ? (
<StickyCTA>
{/* <CancelOfferFees {...props} /> */}
<Button
jumbo
nature='primary'
fullwidth
data-e2e='makeOfferNft'
disabled={disabled}
onClick={() => nftActions.createOffer({ order: activeOrder, ...formValues })}
>
{formValues.amount ? (
<FormattedMessage
id='copy.mark_for_sale'
defaultMessage='Make an Offer for {val}'
values={{
val: `${formValues.amount} ${formValues.coin}`
}}
/>
) : (
<FormattedMessage id='copy.mark_for_sale' defaultMessage='Make an Offer' />
)}
</Button>
</StickyCTA>
) : null}
</>
)
})}
</>
)
}

const mapStateToProps = (state) => ({
formValues: selectors.form.getFormValues('nftCancelOffer')(state) as {
amount: string
coin: string
}
})

const connector = connect(mapStateToProps)

const enhance = compose(
reduxForm<{}, OwnProps>({
form: 'nftCancelOffer',
initialValues: {
coin: 'WETH'
}
}),
connector
)

type Props = OwnProps & ConnectedProps<typeof connector>

export default enhance(CancelOffer) as React.FC<OwnProps>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ModalName } from 'data/types'
import modalEnhancer from 'providers/ModalEnhancer'

import { ModalPropsType } from '../../types'
import CancelOffer from './CancelOffer'
import MakeOffer from './MakeOffer'
import MarkForSale from './MarkForSale'
import ShowAsset from './ShowAsset'
Expand Down Expand Up @@ -68,6 +69,11 @@ class NftOrder extends PureComponent<Props, State> {
<MakeOffer {...this.props} />
</FlyoutChild>
)}
{step === NftOrderStepEnum.CANCEL_OFFER && (
<FlyoutChild>
<CancelOffer {...this.props} />
</FlyoutChild>
)}
{step === NftOrderStepEnum.TRANSFER && (
<FlyoutChild>
<Transfer {...this.props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const MarketForm: React.FC<Props> = (props: Props) => {
{props.marketplace.collection?.name}
</Text>
<Text style={{ marginTop: '8px' }} color='black' weight={600} size='14px'>
<FormattedMessage id='copy.7_day_vol' defaultMessage='7 Day Volume' />
<FormattedMessage id='copy.one_day_vol' defaultMessage='1 Day Volume' />
</Text>
<CoinDisplay
coin='ETH'
Expand Down Expand Up @@ -135,8 +135,8 @@ const MarketForm: React.FC<Props> = (props: Props) => {
value: item.value
}),
[
{ text: 'Volume: High to Low', value: '7_day_vol-DESC' },
{ text: 'Volume: Low to High', value: '7_day_vol-ASC' },
{ text: 'Volume: High to Low', value: 'one_day_vol-DESC' },
{ text: 'Volume: Low to High', value: 'one_day_vol-ASC' },
{ text: 'Floor Price: High to Low', value: 'floor_price-DESC' },
{ text: 'Floor Price: Low to High', value: 'floor_price-ASC' },
{ text: 'Avg. Price: High to Low', value: 'average_price-DESC' },
Expand Down Expand Up @@ -198,5 +198,5 @@ type Props = OwnProps
export default reduxForm<{}, OwnProps>({
destroyOnUnmount: false,
form: 'nftMarketplace',
initialValues: { collection: 'doodles-official', sortBy: '7_day_vol-DESC' }
initialValues: { collection: 'doodles-official', sortBy: 'one_day_vol-DESC' }
})(MarketForm)
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const Offers: React.FC<Props> = (props) => {
<LazyLoadContainer onLazyLoad={() => props.nftsActions.fetchNftOffersMade()}>
{props.offersMade.list.length ? (
props.offersMade.list.map((offer, i) => {
// If user already owns NFT and offer is from them don't show
if (
offer.from_account.address.toLowerCase() === props.defaultEthAddr.toLowerCase() &&
offer.asset.owner.address.toLowerCase() === props.defaultEthAddr.toLowerCase()
Expand Down Expand Up @@ -73,11 +74,26 @@ const Offers: React.FC<Props> = (props) => {
{displayCoinToCoin({ coin: offer.payment_token.symbol, value: offer.bid_amount })}
</div>
{offer.from_account.address.toLowerCase() === props.defaultEthAddr.toLowerCase() ? (
<Button small height='28px' data-e2e='CancelOffer'>
<Button
onClick={() =>
props.nftsActions.nftOrderFlowOpen({ asset: offer.asset, offer })
}
small
height='28px'
data-e2e='CancelOffer'
>
Cancel Offer
</Button>
) : (
<Button small nature='primary' height='28px' data-e2e='AcceptOffer'>
<Button
onClick={() =>
props.nftsActions.nftOrderFlowOpen({ asset: offer.asset, offer })
}
small
nature='primary'
height='28px'
data-e2e='AcceptOffer'
>
Accept Offer
</Button>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default ({ apiUrl, get, post }) => {
}

const getNftCollections = (
sortedBy = '7_day_vol',
sortedBy = 'one_day_vol',
direction = 'DESC'
): ExplorerGatewayNftCollectionType[] => {
return get({
Expand Down

0 comments on commit 5ead33f

Please sign in to comment.