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 Portal from '@reach/portal'
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 styled from 'styled-components/macro'
import { Z_INDEX } from 'theme/zIndex'
@ -76,9 +76,20 @@ export interface PopoverProps {
show: boolean
children: React.ReactNode
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 [popperElement, setPopperElement] = 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,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'offset', options: { offset: [offsetX, offsetY] } },
{ name: 'arrow', options: { element: arrowElement } },
{ name: 'preventOverflow', options: { padding: 8 } },
],
}),
[arrowElement, placement]
[arrowElement, offsetX, offsetY, placement]
)
const { styles, update, attributes } = usePopper(referenceElement, popperElement, options)
@ -105,7 +116,9 @@ export default function Popover({ content, show, children, placement = 'auto' }:
return (
<>
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement>
<ReferenceElement style={style} ref={setReferenceElement as any}>
{children}
</ReferenceElement>
<Portal>
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
{content}

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

@ -163,10 +163,19 @@ interface CardProps {
addAssetToBag: () => void
removeAssetFromBag: () => void
children: ReactNode
isDisabled?: boolean
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 [href, setHref] = useState(baseHref(asset))
@ -210,8 +219,9 @@ const Container = ({ asset, selected, addAssetToBag, removeAssetFromBag, childre
onMouseEnter={() => toggleHovered()}
onMouseLeave={() => toggleHovered()}
transition="250"
cursor={asset.notForSale ? 'default' : 'pointer'}
onClick={onClick ?? handleAssetInBag}
opacity={isDisabled ? '0.5' : '1'}
cursor={isDisabled ? 'default' : 'pointer'}
onClick={isDisabled ? () => null : onClick ?? handleAssetInBag}
>
{children}
</Box>
@ -535,8 +545,14 @@ const ProfileNftDetails = ({ asset, isSellMode }: ProfileNftDetailsProps) => {
</TruncatedTextRow>
{asset.susFlag && <Suspicious />}
</Row>
<TruncatedTextRow className={subhead} style={{ color: themeVars.colors.textSecondary }}>
{!!asset.floorPrice && isSellMode && <span>{`${floorFormatter(asset.floorPrice)} ETH Floor`}</span>}
<TruncatedTextRow
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>
</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 { 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 { WalletAsset } from 'nft/types'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
const NFT_DETAILS_HREF = (asset: WalletAsset) =>
`/nfts/asset/${asset.asset_contract.address}/${asset.tokenId}?origin=profile`
@ -15,6 +19,21 @@ interface ViewMyNftsAssetProps {
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 = ({
asset,
isSellMode,
@ -53,6 +72,10 @@ export const ViewMyNftsAsset = ({
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 (
<Card.Container
asset={asset}
@ -60,16 +83,24 @@ export const ViewMyNftsAsset = ({
addAssetToBag={() => handleSelect(false)}
removeAssetFromBag={() => handleSelect(true)}
onClick={onCardClick}
isDisabled={isDisabled}
>
<Card.ImageContainer>
{assetMediaType === AssetMediaType.Image ? (
<Card.Image />
) : assetMediaType === AssetMediaType.Video ? (
<Card.Video shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} />
) : (
<Card.Audio shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} />
)}
</Card.ImageContainer>
<MouseoverTooltip
text={
<Box as="span" className={bodySmall} style={{ color: themeVars.colors.textPrimary }}>
<Trans>{disabledTooltipText}</Trans>{' '}
</Box>
}
placement="bottom"
offsetX={0}
offsetY={-100}
style={{ display: 'block' }}
disableHover={!isDisabled}
>
<Card.ImageContainer>
{getNftDisplayComponent(assetMediaType, mediaShouldBePlaying, setCurrentTokenPlayingMedia)}
</Card.ImageContainer>
</MouseoverTooltip>
<Card.DetailsContainer>
<Card.ProfileNftDetails asset={asset} isSellMode={isSellMode} />
</Card.DetailsContainer>