Skip to content

Commit

Permalink
feat: offchain bids (#2274)
Browse files Browse the repository at this point in the history
  • Loading branch information
Melisa Anabella Rossi authored Aug 12, 2024
1 parent e2a1f32 commit 8461f71
Show file tree
Hide file tree
Showing 90 changed files with 2,326 additions and 2,280 deletions.
1,584 changes: 146 additions & 1,438 deletions webapp/package-lock.json

Large diffs are not rendered by default.

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.1.0",
"@dcl/single-sign-on-client": "^0.1.0",
"@dcl/ui-env": "^1.5.0",
"@ethersproject/providers": "^5.6.2",
Expand All @@ -23,7 +23,7 @@
"decentraland-connect": "^6.3.1",
"decentraland-crypto-fetch": "^1.0.3",
"decentraland-dapps": "^23.3.0",
"decentraland-transactions": "^2.9.0",
"decentraland-transactions": "^2.10.0",
"decentraland-ui": "^6.6.0",
"ethers": "^5.6.8",
"graphql": "^14.7.0",
Expand Down
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
4 changes: 2 additions & 2 deletions webapp/src/components/AssetPage/Actions/Actions.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { openModal } from 'decentraland-dapps/dist/modules/modal/actions'
import { NFT } from '../../../modules/nft/types'
import { getCurrentOrder } from '../../../modules/order/selectors'
import { RootState } from '../../../modules/reducer'
import { getNFTBids } from '../../../modules/ui/nft/bid/selectors'
import { getAssetBids } from '../../../modules/ui/asset/bid/selectors'
import { getWallet } from '../../../modules/wallet/selectors'
import Actions from './Actions'
import { MapDispatch, MapDispatchProps, MapStateProps, OwnProps } from './Actions.types'

const mapState = (state: RootState): MapStateProps => ({
wallet: getWallet(state),
order: getCurrentOrder(state),
bids: getNFTBids(state)
bids: getAssetBids(state)
})

const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProps => ({
Expand Down
9 changes: 7 additions & 2 deletions webapp/src/components/AssetPage/BaseDetail/BaseDetail.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@
}

.BaseDetail .below {
margin-top: 20px;
margin-top: 48px;
}

.BaseDetail .ui.container {
display: flex;
flex-direction: column;
}

@media (max-width: 768px) {
Expand All @@ -112,7 +117,7 @@
}

.BaseDetail .action-box {
width: 320px;
width: 330px;
}

@media (max-width: 768px) {
Expand Down
8 changes: 4 additions & 4 deletions webapp/src/components/AssetPage/BidList/BidList.container.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { connect } from 'react-redux'
import { fetchBidsByNFTRequest } from '../../../modules/bid/actions'
import { fetchBidsByAssetRequest } from '../../../modules/bid/actions'
import { RootState } from '../../../modules/reducer'
import { getNFTBids } from '../../../modules/ui/nft/bid/selectors'
import { getAssetBids } from '../../../modules/ui/asset/bid/selectors'
import BidList from './BidList'
import { MapStateProps, MapDispatchProps, MapDispatch } from './BidList.types'

const mapState = (state: RootState): MapStateProps => ({
bids: getNFTBids(state)
bids: getAssetBids(state)
})

const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
onFetchBids: nft => dispatch(fetchBidsByNFTRequest(nft))
onFetchBids: nft => dispatch(fetchBidsByAssetRequest(nft))
})

export default connect(mapState, mapDispatch)(BidList)
12 changes: 3 additions & 9 deletions webapp/src/components/AssetPage/BidList/BidList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react'
import React, { useEffect, useState } from 'react'
import { Header } from 'decentraland-ui'
import { Bid } from '../../Bid'
import { Props } from './BidList.types'
Expand All @@ -9,14 +9,8 @@ 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
const filteredBids = useMemo(
() => bids.filter(bid => bid.contractAddress === nft.contractAddress && bid.tokenId === nft.tokenId),
[nft, bids]
)

useEffect(() => {
if (!hasFetched) {
if (!hasFetched && nft) {
setHasFetched(true)
onFetchBids(nft)
}
Expand All @@ -33,7 +27,7 @@ const BidList = (props: Props) => {
<div className="BidList">
<Header sub>Bids</Header>
<div className="list">
{filteredBids.map(bid => (
{bids.map(bid => (
<Bid key={bid.id} bid={bid} isArchivable={false} hasImage={false} />
))}
</div>
Expand Down
6 changes: 3 additions & 3 deletions webapp/src/components/AssetPage/BidList/BidList.types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Dispatch } from 'redux'
import { Bid } from '@dcl/schemas'
import { fetchBidsByNFTRequest, FetchBidsByNFTRequestAction } from '../../../modules/bid/actions'
import { fetchBidsByAssetRequest, FetchBidsByAssetRequestAction } from '../../../modules/bid/actions'
import { NFT } from '../../../modules/nft/types'

export type Props = {
nft: NFT
bids: Bid[]
onFetchBids: typeof fetchBidsByNFTRequest
onFetchBids: typeof fetchBidsByAssetRequest
}

export type MapStateProps = Pick<Props, 'bids'>
export type MapDispatchProps = Pick<Props, 'onFetchBids'>
export type MapDispatch = Dispatch<FetchBidsByNFTRequestAction>
export type MapDispatch = Dispatch<FetchBidsByAssetRequestAction>
export type OwnProps = Pick<Props, 'nft'>
19 changes: 0 additions & 19 deletions webapp/src/components/AssetPage/BidsTable/BidsTable.container.ts

This file was deleted.

139 changes: 13 additions & 126 deletions webapp/src/components/AssetPage/BidsTable/BidsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
import React, { useEffect, useState } from 'react'
import { ethers } from 'ethers'
import { Bid, BidSortBy } from '@dcl/schemas'
import { T, t } from 'decentraland-dapps/dist/modules/translation/utils'
import { Mana, useTabletAndBelowMediaQuery } from 'decentraland-ui'
import { formatWeiMANA } from '../../../lib/mana'
import { AssetType } from '../../../modules/asset/types'
import { getAssetName } from '../../../modules/asset/utils'
import { bidAPI } from '../../../modules/vendor/decentraland'
import { AssetProvider } from '../../AssetProvider'
import { ConfirmInputValueModal } from '../../ConfirmInputValueModal'
import React, { useState } from 'react'
import { BidSortBy } from '@dcl/schemas'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import TableContainer from '../../Table/TableContainer'
import { TableContent } from '../../Table/TableContent'
import { DataTableType } from '../../Table/TableContent/TableContent.types'
import { formatDataToTable } from './utils'
import BidsTableContent from './BidsTableContent/index'
import { Props } from './BidsTable.types'

export const ROWS_PER_PAGE = 5
const INITIAL_PAGE = 1

const BidsTable = (props: Props) => {
const { nft, address, isAcceptingBid, onAccept } = props
const isMobileOrTablet = useTabletAndBelowMediaQuery()

const BidsTable = ({ asset }: Props) => {
const tabList = [
{
value: 'offers_table',
Expand All @@ -44,113 +28,16 @@ const BidsTable = (props: Props) => {
}
]

const [bids, setBids] = useState<DataTableType[]>([])
const [total, setTotal] = useState(0)
const [page, setPage] = useState(INITIAL_PAGE)
const [totalPages, setTotalPages] = useState<number>(0)
const [isLoading, setIsLoading] = useState(false)
const [sortBy, setSortBy] = useState<BidSortBy>(BidSortBy.MOST_EXPENSIVE)
const [showConfirmationModal, setShowConfirmationModal] = useState<{
display: boolean
bid: Bid | null
}>({
display: false,
bid: null
})

// We're doing this outside of redux to avoid having to store all orders when we only care about the first ROWS_PER_PAGE
useEffect(() => {
let cancel = false
if (nft) {
setIsLoading(true)
bidAPI
.fetchByNFT(nft.contractAddress, nft.tokenId, null, sortBy, ROWS_PER_PAGE.toString(), ((page - 1) * ROWS_PER_PAGE).toString())
.then(response => {
if (cancel) return
setTotal(response.total)
setBids(
formatDataToTable(
response.data.filter(bid => bid.bidder !== address),
bid => setShowConfirmationModal({ display: true, bid }),
address,
isMobileOrTablet
)
)
setTotalPages(Math.ceil(response.total / ROWS_PER_PAGE) | 0)
})
.finally(() => !cancel && setIsLoading(false))
.catch(error => {
console.error(error)
})
return () => {
cancel = true
}
}
}, [nft, setIsLoading, setBids, page, sortBy, address, isMobileOrTablet])

return bids.length > 0 ? (
<>
<TableContainer
children={
<TableContent
data={bids}
activePage={page}
isLoading={isLoading}
setPage={setPage}
totalPages={totalPages}
empty={() => null}
total={total}
hasHeaders
/>
}
tabsList={tabList}
sortbyList={sortByList}
handleSortByChange={(value: string) => setSortBy(value as BidSortBy)}
sortBy={sortBy}
/>
{showConfirmationModal.bid && showConfirmationModal.display ? (
<AssetProvider
type={AssetType.NFT}
contractAddress={showConfirmationModal.bid.contractAddress}
tokenId={showConfirmationModal.bid.tokenId}
>
{nft =>
nft &&
showConfirmationModal.bid && (
<ConfirmInputValueModal
open={showConfirmationModal.display}
headerTitle={t('bid_page.confirm.title')}
content={
<>
<T
id="bid_page.confirm.accept_bid_line_one"
values={{
name: <b>{getAssetName(nft)}</b>,
amount: (
<Mana showTooltip network={nft.network} inline>
{formatWeiMANA(showConfirmationModal.bid.price)}
</Mana>
)
}}
/>
<br />
<T id="bid_page.confirm.accept_bid_line_two" />
</>
}
onConfirm={() => {
showConfirmationModal.bid && onAccept(showConfirmationModal.bid)
}}
valueToConfirm={ethers.utils.formatEther(showConfirmationModal.bid.price)}
network={nft.network}
onCancel={() => setShowConfirmationModal({ display: false, bid: null })}
loading={isAcceptingBid}
disabled={isAcceptingBid}
/>
)
}
</AssetProvider>
) : null}
</>
return asset ? (
<TableContainer
children={<BidsTableContent asset={asset} sortBy={sortBy} />}
tabsList={tabList}
sortbyList={sortByList}
handleSortByChange={(value: string) => setSortBy(value as BidSortBy)}
sortBy={sortBy}
/>
) : null
}

Expand Down
Loading

0 comments on commit 8461f71

Please sign in to comment.