Skip to content

Commit

Permalink
feaT: add remove order logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Melisa Anabella Rossi committed Oct 3, 2024
1 parent fa0971b commit 30cd6fd
Show file tree
Hide file tree
Showing 19 changed files with 336 additions and 48 deletions.
9 changes: 5 additions & 4 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@dcl/crypto": "^3.4.5",
"@dcl/hashing": "^3.0.4",
"@dcl/mini-rpc": "^1.0.7",
"@dcl/schemas": "^14.0.0",
"@dcl/schemas": "^14.1.0",
"@dcl/sdk": "7.5.5",
"@dcl/single-sign-on-client": "^0.1.0",
"@dcl/ui-env": "^1.5.0",
Expand Down
5 changes: 5 additions & 0 deletions src/components/CollectionDetailPage/CollectionDetailPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,8 @@
.CollectionDetailPage.popup-mint:before {
background-color: var(--smart-grey) !important;
}

.toast-info .body {
max-width: 450px;
overflow: hidden;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,40 @@ import { connect } from 'react-redux'
import { getAddress } from 'decentraland-dapps/dist/modules/wallet/selectors'
import { RootState } from 'modules/common/types'
import { openModal } from 'decentraland-dapps/dist/modules/modal/actions'
import { deleteItemRequest } from 'modules/item/actions'
import { getStatusByItemId } from 'modules/item/selectors'
import {
CANCEL_ITEM_ORDER_TRADE_REQUEST,
cancelItemOrderTradeRequest,
CancelItemOrderTradeRequestAction,
deleteItemRequest
} from 'modules/item/actions'
import { getLoading, getStatusByItemId } from 'modules/item/selectors'
import { setItems } from 'modules/editor/actions'
import { MapStateProps, MapDispatch, MapDispatchProps, OwnProps } from './CollectionItem.types'
import CollectionItem from './CollectionItem'
import { getIsOffchainPublicItemOrdersEnabled } from 'modules/features/selectors'
import { getWallet } from 'modules/wallet/selectors'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading'

const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => {
const statusByItemId = getStatusByItemId(state)

const loadingTradeIds = getLoading(state)
.filter(action => action.type === CANCEL_ITEM_ORDER_TRADE_REQUEST)
.map(action => (action as CancelItemOrderTradeRequestAction).payload.tradeId)
return {
ethAddress: getAddress(state),
status: statusByItemId[ownProps.item.id],
wallet: getWallet(state),
isOffchainPublicItemOrdersEnabled: getIsOffchainPublicItemOrdersEnabled(state)
isOffchainPublicItemOrdersEnabled: getIsOffchainPublicItemOrdersEnabled(state),
isCancellingItemOrder: isLoadingType(getLoading(state), CANCEL_ITEM_ORDER_TRADE_REQUEST),
loadingTradeIds
}
}

const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
onOpenModal: (name, metadata) => dispatch(openModal(name, metadata)),
onDeleteItem: item => dispatch(deleteItemRequest(item)),
onSetItems: items => dispatch(setItems(items))
onSetItems: items => dispatch(setItems(items)),
onRemoveFromSale: tradeId => dispatch(cancelItemOrderTradeRequest(tradeId, true))
})

export default connect(mapState, mapDispatch)(CollectionItem)
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ const LENGTH_LIMIT = 25
export default function CollectionItem({
onOpenModal,
onSetItems,
onRemoveFromSale,
item,
isOffchainPublicItemOrdersEnabled,
collection,
status,
ethAddress,
wallet
wallet,
loadingTradeIds,
isCancellingItemOrder
}: Props) {
analytics = getAnalytics()
const history = useHistory()
Expand All @@ -39,6 +42,11 @@ export default function CollectionItem({
const shouldAllowPriceEdition = !isOffchainPublicItemOrdersEnabled || isEnableForSaleOffchainMarketplace || isOnSaleLegacy

const handleEditPriceAndBeneficiary = useCallback(() => {
if (isOffchainPublicItemOrdersEnabled && isEnableForSaleOffchainMarketplace) {
onOpenModal('PutForSaleOffchainModal', { itemId: item.id })
return
}

onOpenModal('EditPriceAndBeneficiaryModal', { itemId: item.id })
}, [item, onOpenModal])

Expand Down Expand Up @@ -69,6 +77,13 @@ export default function CollectionItem({
onOpenModal('PutForSaleOffchainModal', { itemId: item.id })
}, [])

const handleRemoveFromSale = useCallback(() => {
if (!item.tradeId) {
return
}
onRemoveFromSale(item.tradeId)
}, [])

const renderPrice = useCallback(() => {
if (!item.price) {
return (
Expand All @@ -78,20 +93,20 @@ export default function CollectionItem({
)
}

if (isFree(item)) {
return <span>{t('global.free')}</span>
if (item.price === ethers.constants.MaxUint256.toString() || (isOffchainPublicItemOrdersEnabled && !isOnSaleLegacy && !item.tradeId)) {
return <span>-</span>
}

if (item.price === ethers.constants.MaxUint256.toString()) {
return <span>-</span>
if (isFree(item)) {
return <span>{t('global.free')}</span>
}

return (
<Mana className={styles.mana} network={Network.MATIC} showTooltip>
{ethers.utils.formatEther(item.price)}
</Mana>
)
}, [item, handleEditPriceAndBeneficiary])
}, [item, isOnSaleLegacy, isOffchainPublicItemOrdersEnabled, handleEditPriceAndBeneficiary])

const renderItemStatus = useCallback(() => {
return status === SyncStatus.UNSYNCED ? (
Expand Down Expand Up @@ -214,13 +229,25 @@ export default function CollectionItem({
</Table.Cell>
) : null}
<Table.Cell>{renderItemStatus()}</Table.Cell>
{isOffchainPublicItemOrdersEnabled && !isOnSaleLegacy && (
{isOffchainPublicItemOrdersEnabled && !isOnSaleLegacy && !item.tradeId && (
<Table.Cell>
<Button primary size="tiny" disabled={!isEnableForSaleOffchainMarketplace} onClick={handlePutForSale}>
{t('collection_item.put_for_sale')}
</Button>
</Table.Cell>
)}
{isOffchainPublicItemOrdersEnabled && item.tradeId && (
<Table.Cell>
<Button
secondary
size="tiny"
onClick={handleRemoveFromSale}
loading={isCancellingItemOrder && loadingTradeIds.includes(item.tradeId)}
>
{t('collection_item.remove_from_sale')}
</Button>
</Table.Cell>
)}
<Table.Cell className={styles.contextMenuButton}>{renderItemContextMenu()}</Table.Cell>
</Table.Row>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Dispatch } from 'redux'
import { Collection } from 'modules/collection/types'
import { Item, SyncStatus } from 'modules/item/types'
import { openModal, OpenModalAction } from 'decentraland-dapps/dist/modules/modal/actions'
import { deleteItemRequest, DeleteItemRequestAction } from 'modules/item/actions'
import { CancelItemOrderTradeRequestAction, deleteItemRequest, DeleteItemRequestAction } from 'modules/item/actions'
import { setItems, SetItemsAction } from 'modules/editor/actions'
import { Wallet } from 'decentraland-dapps/dist/modules/wallet'

Expand All @@ -13,12 +13,18 @@ export type Props = {
status: SyncStatus
isOffchainPublicItemOrdersEnabled: boolean
wallet: Wallet | null
isCancellingItemOrder: boolean
loadingTradeIds: string[]
onOpenModal: typeof openModal
onDeleteItem: typeof deleteItemRequest
onSetItems: typeof setItems
onRemoveFromSale: (tradeId: string) => void
}

export type MapStateProps = Pick<Props, 'ethAddress' | 'status' | 'isOffchainPublicItemOrdersEnabled' | 'wallet'>
export type MapDispatchProps = Pick<Props, 'onOpenModal' | 'onDeleteItem' | 'onSetItems'>
export type MapDispatch = Dispatch<OpenModalAction | DeleteItemRequestAction | SetItemsAction>
export type MapStateProps = Pick<
Props,
'ethAddress' | 'status' | 'isOffchainPublicItemOrdersEnabled' | 'wallet' | 'isCancellingItemOrder' | 'loadingTradeIds'
>
export type MapDispatchProps = Pick<Props, 'onOpenModal' | 'onDeleteItem' | 'onSetItems' | 'onRemoveFromSale'>
export type MapDispatch = Dispatch<OpenModalAction | DeleteItemRequestAction | SetItemsAction | CancelItemOrderTradeRequestAction>
export type OwnProps = Pick<Props, 'item'>
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export default class EditPriceAndBeneficiaryModal extends React.PureComponent<Pr

getItemPrice() {
const { item } = this.props
return item.price ? ethers.utils.formatEther(item.price) : undefined
return item.price && item.price !== ethers.constants.MaxUint256.toString() ? ethers.utils.formatEther(item.price) : undefined
}

isDisabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { RootState } from 'modules/common/types'
import { getAuthorizedItems, getError, getLoading } from 'modules/item/selectors'
import { OwnProps, MapStateProps, MapDispatch, MapDispatchProps } from './PutForSaleOffchainModal.types'
import PutForSaleOffchainModal from './PutForSaleOffchainModal'
import { CREATE_ITEM_ORDER_TRADE_REQUEST, createItemOrderTradeRequest } from 'modules/item/actions'
import {
CANCEL_ITEM_ORDER_TRADE_REQUEST,
cancelItemOrderTradeRequest,
CREATE_ITEM_ORDER_TRADE_REQUEST,
createItemOrderTradeRequest
} from 'modules/item/actions'
import { Item } from 'modules/item/types'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading'
import { getAuthorizedCollections } from 'modules/collection/selectors'
Expand All @@ -21,13 +26,15 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => {
item,
collection,
isLoading: isLoadingType(getLoading(state), CREATE_ITEM_ORDER_TRADE_REQUEST),
isLoadingCancel: isLoadingType(getLoading(state), CANCEL_ITEM_ORDER_TRADE_REQUEST),
error: getError(state)
}
}

const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
onCreateItemOrder: (item: Item, priceInWei: string, beneficiary: string, collection: Collection, expiresAt: Date) =>
dispatch(createItemOrderTradeRequest(item, priceInWei, beneficiary, collection, expiresAt))
dispatch(createItemOrderTradeRequest(item, priceInWei, beneficiary, collection, expiresAt)),
onRemoveFromSale: (tradeId: string) => dispatch(cancelItemOrderTradeRequest(tradeId))
})

export default connect(mapState, mapDispatch)(PutForSaleOffchainModal)
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.modalTitle {
font-size: 20px;
font-weight: 700;
line-height: 32px;
text-align: center;
}

.modalSubtitle {
font-size: 16px;
line-height: 24px;
text-align: center;
}

:global(.PutForSaleOffchainModal.ui.modal > .content) {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.modalContent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 500px;
padding: 40px 0;
}

.modalIcon {
background: var(--secondary-text);
border-radius: 50%;
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
}

.modalIcon :global(i.icon) {
margin: 0;
}

.error {
max-width: 100%;
overflow: hidden;
color: var(--error);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { Props } from './PutForSaleOffchainModal.types'
import Modal from 'decentraland-dapps/dist/containers/Modal'
import EditPriceAndBeneficiaryModal from '../EditPriceAndBeneficiaryModal/EditPriceAndBeneficiaryModal'
import { Item } from 'modules/item/types'
import { Button, Icon, Loader, ModalActions, ModalContent } from 'decentraland-ui'
import styles from './PutForSaleOffchainModal.module.css'
import { t } from 'decentraland-dapps/dist/modules/translation'

export default function PutForSaleOffchainModal({ item, collection, error, metadata, isLoading, onClose, onCreateItemOrder }: Props) {
export default function PutForSaleOffchainModal({
item,
collection,
error,
metadata,
isLoading,
isLoadingCancel,
onClose,
onCreateItemOrder,
onRemoveFromSale
}: Props) {
const handlePutForSale = (_itemId: string, price: string, beneficiary: string, expiresAt = new Date()) => {
if (!collection || !item) {
console.error('Collection or item not found')
Expand All @@ -11,6 +25,35 @@ export default function PutForSaleOffchainModal({ item, collection, error, metad
onCreateItemOrder(item as Item, price, beneficiary, collection, expiresAt)
}

const handleRemoveFromSale = () => {
onRemoveFromSale(item.tradeId!)
}

if (item.tradeId) {
return (
<Modal name="PutForSaleOffchainModal" onClose={onClose} size="small">
<ModalContent>
<div className={styles.modalContent}>
<div className={styles.modalIcon}>
{isLoadingCancel ? <Loader active size="large" inline /> : <Icon name="tag" size="big" />}
</div>
<h2 className={styles.modalTitle}>{t('put_for_sale_offchain_modal.title')}</h2>
<p className={styles.modalSubtitle}>{t('put_for_sale_offchain_modal.subtitle')}</p>
</div>
{error ? <span className={styles.error}>{error}</span> : null}
</ModalContent>
<ModalActions>
<Button onClick={onClose} disabled={isLoadingCancel}>
{t('put_for_sale_offchain_modal.cancel')}
</Button>
<Button primary onClick={handleRemoveFromSale} disabled={isLoadingCancel} loading={isLoadingCancel}>
{t('put_for_sale_offchain_modal.reject_old_prices')}
</Button>
</ModalActions>
</Modal>
)
}

return (
<EditPriceAndBeneficiaryModal
name="EditPriceAndBeneficiaryModal"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types'
import { Collection } from 'modules/collection/types'
import { createItemOrderTradeRequest, CreateItemOrderTradeRequestAction } from 'modules/item/actions'
import {
cancelItemOrderTradeRequest,
CancelItemOrderTradeRequestAction,
createItemOrderTradeRequest,
CreateItemOrderTradeRequestAction
} from 'modules/item/actions'
import { Item, ItemType } from 'modules/item/types'
import { Dispatch } from 'redux'

export type Props = ModalProps & {
item: Item<ItemType.WEARABLE | ItemType.EMOTE>
isLoading: boolean
isLoadingCancel: boolean
error: string | null
collection?: Collection
onCreateItemOrder: typeof createItemOrderTradeRequest
onRemoveFromSale: typeof cancelItemOrderTradeRequest
}

export type EditPriceAndBeneficiaryModalMetadata = {
itemId: string
}

export type OwnProps = Pick<Props, 'metadata' | 'item'>
export type MapStateProps = Pick<Props, 'item' | 'isLoading' | 'error' | 'collection'>
export type MapDispatchProps = Pick<Props, 'onCreateItemOrder'>
export type MapDispatch = Dispatch<CreateItemOrderTradeRequestAction>
export type MapStateProps = Pick<Props, 'item' | 'isLoading' | 'error' | 'collection' | 'isLoadingCancel'>
export type MapDispatchProps = Pick<Props, 'onCreateItemOrder' | 'onRemoveFromSale'>
export type MapDispatch = Dispatch<CreateItemOrderTradeRequestAction | CancelItemOrderTradeRequestAction>
Loading

0 comments on commit 30cd6fd

Please sign in to comment.