feat: Web 1854 listed card (#5160)

* init

* it's working with jack's card.tsx components

* add nft details on cards for view my nfts

* listed cards ready for review

* remove unnecessary code

* updated radius

* first round charlie comments

* respond all comments

* init

* fix

* color

* remove floor price when not on sell mode

* remove floor when nft is listed

* feat: Web 1858 disabled card 1155 when sell mode is on (#5169)

* disabled states + tooltips

* remove collection asset changes

* popover offset changes

* respond to padmini comment

* respond to charlie
This commit is contained in:
lynn 2022-11-11 13:22:19 -05:00 committed by GitHub
parent a95697daf8
commit f1443671ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 21 deletions

@ -1,7 +1,7 @@
import { Options, Placement } from '@popperjs/core' import { Options, Placement } from '@popperjs/core'
import Portal from '@reach/portal' import Portal from '@reach/portal'
import useInterval from 'lib/hooks/useInterval' import useInterval from 'lib/hooks/useInterval'
import React, { useCallback, useMemo, useState } from 'react' import React, { CSSProperties, useCallback, useMemo, useState } from 'react'
import { usePopper } from 'react-popper' import { usePopper } from 'react-popper'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { Z_INDEX } from 'theme/zIndex' import { Z_INDEX } from 'theme/zIndex'
@ -76,9 +76,20 @@ export interface PopoverProps {
show: boolean show: boolean
children: React.ReactNode children: React.ReactNode
placement?: Placement placement?: Placement
offsetX?: number
offsetY?: number
style?: CSSProperties
} }
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) { export default function Popover({
content,
show,
children,
placement = 'auto',
offsetX = 8,
offsetY = 8,
style,
}: PopoverProps) {
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null) const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null)
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null) const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null) const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
@ -88,12 +99,12 @@ export default function Popover({ content, show, children, placement = 'auto' }:
placement, placement,
strategy: 'fixed', strategy: 'fixed',
modifiers: [ modifiers: [
{ name: 'offset', options: { offset: [8, 8] } }, { name: 'offset', options: { offset: [offsetX, offsetY] } },
{ name: 'arrow', options: { element: arrowElement } }, { name: 'arrow', options: { element: arrowElement } },
{ name: 'preventOverflow', options: { padding: 8 } }, { name: 'preventOverflow', options: { padding: 8 } },
], ],
}), }),
[arrowElement, placement] [arrowElement, offsetX, offsetY, placement]
) )
const { styles, update, attributes } = usePopper(referenceElement, popperElement, options) const { styles, update, attributes } = usePopper(referenceElement, popperElement, options)
@ -105,7 +116,9 @@ export default function Popover({ content, show, children, placement = 'auto' }:
return ( return (
<> <>
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement> <ReferenceElement style={style} ref={setReferenceElement as any}>
{children}
</ReferenceElement>
<Portal> <Portal>
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}> <PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
{content} {content}

@ -42,9 +42,10 @@ export function MouseoverTooltip({ text, disableHover, children, ...rest }: Omit
const [show, setShow] = useState(false) const [show, setShow] = useState(false)
const open = useCallback(() => setShow(true), [setShow]) const open = useCallback(() => setShow(true), [setShow])
const close = useCallback(() => setShow(false), [setShow]) const close = useCallback(() => setShow(false), [setShow])
const noOp = () => null
return ( return (
<Tooltip {...rest} show={show} text={disableHover ? null : text}> <Tooltip {...rest} show={show} text={disableHover ? null : text}>
<div onMouseEnter={open} onMouseLeave={close}> <div onMouseEnter={disableHover ? noOp : open} onMouseLeave={disableHover ? noOp : close}>
{children} {children}
</div> </div>
</Tooltip> </Tooltip>

@ -163,10 +163,19 @@ interface CardProps {
addAssetToBag: () => void addAssetToBag: () => void
removeAssetFromBag: () => void removeAssetFromBag: () => void
children: ReactNode children: ReactNode
isDisabled?: boolean
onClick?: () => void onClick?: () => void
} }
const Container = ({ asset, selected, addAssetToBag, removeAssetFromBag, children, onClick }: CardProps) => { const Container = ({
asset,
selected,
addAssetToBag,
removeAssetFromBag,
children,
isDisabled,
onClick,
}: CardProps) => {
const [hovered, toggleHovered] = useReducer((s) => !s, false) const [hovered, toggleHovered] = useReducer((s) => !s, false)
const [href, setHref] = useState(baseHref(asset)) const [href, setHref] = useState(baseHref(asset))
@ -210,8 +219,9 @@ const Container = ({ asset, selected, addAssetToBag, removeAssetFromBag, childre
onMouseEnter={() => toggleHovered()} onMouseEnter={() => toggleHovered()}
onMouseLeave={() => toggleHovered()} onMouseLeave={() => toggleHovered()}
transition="250" transition="250"
cursor={asset.notForSale ? 'default' : 'pointer'} opacity={isDisabled ? '0.5' : '1'}
onClick={onClick ?? handleAssetInBag} cursor={isDisabled ? 'default' : 'pointer'}
onClick={isDisabled ? () => null : onClick ?? handleAssetInBag}
> >
{children} {children}
</Box> </Box>
@ -535,8 +545,14 @@ const ProfileNftDetails = ({ asset, isSellMode }: ProfileNftDetailsProps) => {
</TruncatedTextRow> </TruncatedTextRow>
{asset.susFlag && <Suspicious />} {asset.susFlag && <Suspicious />}
</Row> </Row>
<TruncatedTextRow className={subhead} style={{ color: themeVars.colors.textSecondary }}> <TruncatedTextRow
{!!asset.floorPrice && isSellMode && <span>{`${floorFormatter(asset.floorPrice)} ETH Floor`}</span>} className={subhead}
style={{ color: !asset.notForSale ? themeVars.colors.textPrimary : themeVars.colors.textSecondary }}
>
{!asset.notForSale && <span>{`${floorFormatter(asset.floor_sell_order_price)} ETH`}</span>}
{asset.notForSale && isSellMode && !!asset.floorPrice && (
<span>{`${floorFormatter(asset.floorPrice)} ETH Floor`}</span>
)}
</TruncatedTextRow> </TruncatedTextRow>
</Box> </Box>
) )

@ -1,10 +1,14 @@
import { Trans } from '@lingui/macro'
import { MouseoverTooltip } from 'components/Tooltip'
import { Box } from 'nft/components/Box'
import * as Card from 'nft/components/collection/Card' import * as Card from 'nft/components/collection/Card'
import { AssetMediaType } from 'nft/components/collection/Card' import { AssetMediaType } from 'nft/components/collection/Card'
import { bodySmall } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useBag, useIsMobile, useSellAsset } from 'nft/hooks' import { useBag, useIsMobile, useSellAsset } from 'nft/hooks'
import { WalletAsset } from 'nft/types' import { WalletAsset } from 'nft/types'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const NFT_DETAILS_HREF = (asset: WalletAsset) => const NFT_DETAILS_HREF = (asset: WalletAsset) =>
`/nfts/asset/${asset.asset_contract.address}/${asset.tokenId}?origin=profile` `/nfts/asset/${asset.asset_contract.address}/${asset.tokenId}?origin=profile`
@ -15,6 +19,21 @@ interface ViewMyNftsAssetProps {
setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void
} }
const getNftDisplayComponent = (
assetMediaType: AssetMediaType,
mediaShouldBePlaying: boolean,
setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void
) => {
switch (assetMediaType) {
case AssetMediaType.Image:
return <Card.Image />
case AssetMediaType.Video:
return <Card.Video shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} />
case AssetMediaType.Audio:
return <Card.Audio shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} />
}
}
export const ViewMyNftsAsset = ({ export const ViewMyNftsAsset = ({
asset, asset,
isSellMode, isSellMode,
@ -53,6 +72,10 @@ export const ViewMyNftsAsset = ({
const assetMediaType = Card.useAssetMediaType(asset) const assetMediaType = Card.useAssetMediaType(asset)
const isDisabled = asset.asset_contract.tokenType === 'ERC1155' || (isSellMode && asset.susFlag)
const disabledTooltipText =
asset.asset_contract.tokenType === 'ERC1155' ? 'ERC-1155 support coming soon' : 'Blocked from trading'
return ( return (
<Card.Container <Card.Container
asset={asset} asset={asset}
@ -60,16 +83,24 @@ export const ViewMyNftsAsset = ({
addAssetToBag={() => handleSelect(false)} addAssetToBag={() => handleSelect(false)}
removeAssetFromBag={() => handleSelect(true)} removeAssetFromBag={() => handleSelect(true)}
onClick={onCardClick} onClick={onCardClick}
isDisabled={isDisabled}
> >
<Card.ImageContainer> <MouseoverTooltip
{assetMediaType === AssetMediaType.Image ? ( text={
<Card.Image /> <Box as="span" className={bodySmall} style={{ color: themeVars.colors.textPrimary }}>
) : assetMediaType === AssetMediaType.Video ? ( <Trans>{disabledTooltipText}</Trans>{' '}
<Card.Video shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} /> </Box>
) : ( }
<Card.Audio shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} /> placement="bottom"
)} offsetX={0}
</Card.ImageContainer> offsetY={-100}
style={{ display: 'block' }}
disableHover={!isDisabled}
>
<Card.ImageContainer>
{getNftDisplayComponent(assetMediaType, mediaShouldBePlaying, setCurrentTokenPlayingMedia)}
</Card.ImageContainer>
</MouseoverTooltip>
<Card.DetailsContainer> <Card.DetailsContainer>
<Card.ProfileNftDetails asset={asset} isSellMode={isSellMode} /> <Card.ProfileNftDetails asset={asset} isSellMode={isSellMode} />
</Card.DetailsContainer> </Card.DetailsContainer>