Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add accept trade flow #2268

Merged
merged 8 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions webapp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"@0xsquid/squid-types": "^0.1.29",
"@covalenthq/client-sdk": "^0.6.4",
"@dcl/crypto": "^3.0.0",
"@dcl/schemas": "^11.10.5",
"@dcl/schemas": "^12.0.0",
"@dcl/single-sign-on-client": "^0.1.0",
"@dcl/ui-env": "^1.5.0",
"@ethersproject/providers": "^5.6.2",
Expand Down Expand Up @@ -123,4 +123,4 @@
"type": "git",
"url": "https://github.com/decentraland/marketplace.git"
}
}
}
81 changes: 21 additions & 60 deletions webapp/src/components/ActivityPage/Transaction/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,73 +220,34 @@ const Transaction = (props: Props) => {
</AssetProvider>
)
}
case PLACE_BID_SUCCESS: {
const { tokenId, contractAddress, price } = tx.payload as { tokenId: string; contractAddress: string; price: number }

return (
<AssetProvider type={AssetType.NFT} contractAddress={contractAddress} tokenId={tokenId}>
{nft => (
<TransactionDetail
asset={nft}
text={
<T
id="transaction.detail.place_bid"
values={{
name: <Link to={locations.nft(contractAddress, tokenId)}>{nft ? getAssetName(nft) : ''}</Link>,
price: (
<Mana showTooltip network={nft?.network} inline>
{price.toLocaleString()}
</Mana>
)
}}
/>
}
tx={tx}
/>
)}
</AssetProvider>
)
}
case ACCEPT_BID_TRANSACTION_SUBMITTED: {
const { tokenId, contractAddress, price } = tx.payload as { tokenId: string; contractAddress: string; price: number }
return (
<AssetProvider type={AssetType.NFT} contractAddress={contractAddress} tokenId={tokenId}>
{nft => (
<TransactionDetail
asset={nft}
text={
<T
id="transaction.detail.accept_bid"
values={{
name: <Link to={locations.nft(contractAddress, tokenId)}>{nft ? getAssetName(nft) : ''}</Link>,
price: (
<Mana showTooltip inline network={nft?.network}>
{price.toLocaleString()}
</Mana>
)
}}
/>
}
tx={tx}
/>
)}
</AssetProvider>
)
}
case PLACE_BID_SUCCESS:
case ACCEPT_BID_TRANSACTION_SUBMITTED:
case CANCEL_BID_SUCCESS: {
const { tokenId, contractAddress, price } = tx.payload as { tokenId: string; contractAddress: string; price: number }
const payload = tx.payload as { contractAddress: string; price: number } & ({ tokenId: string } | { itemId: string })
const { contractAddress, price } = payload

const tokenId = 'tokenId' in payload ? payload.tokenId : payload.itemId
const assetType = 'tokenId' in payload ? AssetType.NFT : AssetType.ITEM
const link = assetType === AssetType.NFT ? locations.nft(contractAddress, tokenId) : locations.item(contractAddress, tokenId)
const translationKey =
tx.actionType === PLACE_BID_SUCCESS
? 'transaction.detail.place_bid'
: tx.actionType === ACCEPT_BID_TRANSACTION_SUBMITTED
? 'transaction.detail.accept_bid'
: 'transaction.detail.cancel_bid'

return (
<AssetProvider type={AssetType.NFT} contractAddress={contractAddress} tokenId={tokenId}>
{nft => (
<AssetProvider type={assetType} contractAddress={contractAddress} tokenId={tokenId}>
{asset => (
<TransactionDetail
asset={nft}
asset={asset}
text={
<T
id="transaction.detail.cancel_bid"
id={translationKey}
values={{
name: <Link to={locations.nft(contractAddress, tokenId)}>{nft ? getAssetName(nft) : ''}</Link>,
name: <Link to={link}>{asset ? getAssetName(asset) : ''}</Link>,
price: (
<Mana showTooltip inline network={nft?.network}>
<Mana showTooltip network={asset?.network} inline>
{price.toLocaleString()}
</Mana>
)
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/components/AssetPage/BidList/BidList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ const BidList = (props: Props) => {
const [hasFetched, setHasFetched] = useState(false)

// this is because when you change from one nft detail to another you would still see the previous nft bids
// TODO: Support new bids from trades
const filteredBids = useMemo(
() => bids.filter(bid => bid.contractAddress === nft.contractAddress && bid.tokenId === nft.tokenId),
() => bids.filter(bid => bid.contractAddress === nft.contractAddress && 'tokenId' in bid && bid.tokenId === nft.tokenId),
[nft, bids]
)

Expand Down
3 changes: 2 additions & 1 deletion webapp/src/components/AssetPage/BidsTable/BidsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Mana, useTabletAndBelowMediaQuery } from 'decentraland-ui'
import { formatWeiMANA } from '../../../lib/mana'
import { AssetType } from '../../../modules/asset/types'
import { getAssetName } from '../../../modules/asset/utils'
import { isBidTrade } from '../../../modules/bid/utils'
import { bidAPI } from '../../../modules/vendor/decentraland'
import { AssetProvider } from '../../AssetProvider'
import { ConfirmInputValueModal } from '../../ConfirmInputValueModal'
Expand Down Expand Up @@ -108,7 +109,7 @@ const BidsTable = (props: Props) => {
handleSortByChange={(value: string) => setSortBy(value as BidSortBy)}
sortBy={sortBy}
/>
{showConfirmationModal.bid && showConfirmationModal.display ? (
{showConfirmationModal.bid && showConfirmationModal.display && !isBidTrade(showConfirmationModal.bid) ? (
<AssetProvider
type={AssetType.NFT}
contractAddress={showConfirmationModal.bid.contractAddress}
Expand Down
9 changes: 6 additions & 3 deletions webapp/src/components/AssetPage/YourOffer/YourOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import iconListings from '../../../images/iconListings.png'
import infoIcon from '../../../images/infoIcon.png'
import { formatDistanceToNow } from '../../../lib/date'
import { formatWeiMANA } from '../../../lib/mana'
import { isBidTrade } from '../../../modules/bid/utils'
import { locations } from '../../../modules/routing/locations'
import { bidAPI } from '../../../modules/vendor/decentraland'
import Mana from '../../Mana/Mana'
Expand Down Expand Up @@ -127,9 +128,11 @@ const YourOffer = (props: Props) => {
<Button inverted fluid className={styles.actions} onClick={() => onCancel(bid)}>
{t('offers_table.remove')}
</Button>
<Button primary fluid className={styles.actions} onClick={() => history.push(locations.bid(bid.contractAddress, bid.tokenId))}>
{t('global.update')}
</Button>
{!isBidTrade(bid) && (
<Button primary fluid className={styles.actions} onClick={() => history.push(locations.bid(bid.contractAddress, bid.tokenId))}>
{t('global.update')}
</Button>
)}
</div>
</div>
</div>
Expand Down
14 changes: 8 additions & 6 deletions webapp/src/components/Bid/AcceptButton/AcceptButton.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import React, { useEffect, useState } from 'react'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { Button, Popup } from 'decentraland-ui'
import { isNFT } from '../../../modules/asset/utils'
import { isInsufficientMANA, checkFingerprint } from '../../../modules/bid/utils'
import { useFingerprint } from '../../../modules/nft/hooks'
import { isLandLocked } from '../../../modules/rental/utils'
import { LandLockedPopup } from '../../LandLockedPopup'
import { Props } from './AcceptButton.types'

const AcceptButton = (props: Props) => {
const { nft, bid, onClick, rental, userAddress } = props
const { asset, bid, onClick, rental, userAddress } = props

const [fingerprint, isLoadingFingerprint] = useFingerprint(nft)
const [fingerprint, isLoadingFingerprint] = useFingerprint(asset && isNFT(asset) ? asset : null)
const [hasInsufficientMANA, setHasInsufficientMANA] = useState(false)
const isCurrentlyLocked = rental && nft && isLandLocked(userAddress, rental, nft)
const isCurrentlyLocked = rental && asset && isLandLocked(userAddress, rental, asset)

useEffect(() => {
isInsufficientMANA(bid)
Expand All @@ -21,9 +22,10 @@ const AcceptButton = (props: Props) => {
}, [bid])

const isValidFingerprint = checkFingerprint(bid, fingerprint)
const isValidSeller = !!nft && nft.owner === bid.seller
const assetOwner = !!asset && (isNFT(asset) ? asset.owner : asset?.creator)
const isValidSeller = assetOwner && assetOwner === userAddress

const isDisabled = isCurrentlyLocked || !nft || isLoadingFingerprint || hasInsufficientMANA || !isValidFingerprint || !isValidSeller
const isDisabled = isCurrentlyLocked || !asset || isLoadingFingerprint || hasInsufficientMANA || !isValidFingerprint || !isValidSeller

let button = (
<Button primary disabled={isDisabled} onClick={onClick}>
Expand Down Expand Up @@ -51,7 +53,7 @@ const AcceptButton = (props: Props) => {
button = <Popup content={t('bid.invalid_seller')} position="top center" trigger={<div className="popup-button">{button}</div>} />
} else if (isCurrentlyLocked) {
button = (
<LandLockedPopup asset={nft} rental={rental} userAddress={userAddress}>
<LandLockedPopup asset={asset} rental={rental} userAddress={userAddress}>
{button}
</LandLockedPopup>
)
Expand Down
4 changes: 2 additions & 2 deletions webapp/src/components/Bid/AcceptButton/AcceptButton.types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Bid, RentalListing } from '@dcl/schemas'
import { NFT } from '../../../modules/nft/types'
import { Asset } from '../../../modules/asset/types'

export type Props = {
nft: NFT | null
asset: Asset | null
rental: RentalListing | null
bid: Bid
userAddress: string
Expand Down
7 changes: 6 additions & 1 deletion webapp/src/components/Bid/Bid.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ import { connect } from 'react-redux'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
import { cancelBidRequest, archiveBid, unarchiveBid, acceptBidRequest, ACCEPT_BID_REQUEST } from '../../modules/bid/actions'
import { getLoading } from '../../modules/bid/selectors'
import { getContract } from '../../modules/contract/selectors'
import { getIsBidsOffChainEnabled } from '../../modules/features/selectors'
import { RootState } from '../../modules/reducer'
import { getArchivedBidIds } from '../../modules/ui/nft/bid/selectors'
import { Contract } from '../../modules/vendor/services'
import { getWallet } from '../../modules/wallet/selectors'
import Bid from './Bid'
import { MapStateProps, MapDispatchProps, MapDispatch } from './Bid.types'

const mapState = (state: RootState): MapStateProps => ({
wallet: getWallet(state),
archivedBidIds: getArchivedBidIds(state),
isAcceptingBid: isLoadingType(getLoading(state), ACCEPT_BID_REQUEST)
isAcceptingBid: isLoadingType(getLoading(state), ACCEPT_BID_REQUEST),
isBidsOffchainEnabled: getIsBidsOffChainEnabled(state),
getContract: (query: Partial<Contract>) => getContract(state, query)
})

const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
Expand Down
Loading
Loading