Commit 1536e187 authored by Jack Short's avatar Jack Short Committed by GitHub

style: updating details (#5371)

* style: adjusting details page

* moving owners

* removing traits if no traits

* adding accent colors

* trait grid breakpoint

* some comments

* addressing comments
parent c19431eb
......@@ -6,7 +6,6 @@ import { reduceFilters } from 'nft/components/collection/Activity'
import { LoadingSparkle } from 'nft/components/common/Loading/LoadingSparkle'
import { AssetPriceDetails } from 'nft/components/details/AssetPriceDetails'
import { Center } from 'nft/components/Flex'
import { VerifiedIcon } from 'nft/components/icons'
import { ActivityFetcher } from 'nft/queries/genie/ActivityFetcher'
import { ActivityEventResponse, ActivityEventType, CollectionInfoForAsset, GenieAsset } from 'nft/types'
import { shortenAddress } from 'nft/utils/address'
......@@ -27,17 +26,6 @@ import DetailsContainer from './DetailsContainer'
import InfoContainer from './InfoContainer'
import TraitsContainer from './TraitsContainer'
const CollectionHeader = styled.span`
display: flex;
align-items: center;
font-size: 16px;
line-height: 24px;
color: ${({ theme }) => theme.textPrimary};
margin-top: 28px;
text-decoration: none;
${OpacityHoverState};
`
const AssetPriceDetailsContainer = styled.div`
margin-top: 20px;
display: none;
......@@ -46,15 +34,6 @@ const AssetPriceDetailsContainer = styled.div`
}
`
const AssetHeader = styled.div`
display: flex;
align-items: center;
font-size: 36px;
line-height: 36px;
color: ${({ theme }) => theme.textPrimary};
margin-top: 8px;
`
const MediaContainer = styled.div`
display: flex;
justify-content: center;
......@@ -118,10 +97,6 @@ const Link = styled(RouterLink)`
${OpacityHoverState};
`
const DefaultLink = styled(RouterLink)`
text-decoration: none;
`
const ActivitySelectContainer = styled.div`
display: flex;
gap: 8px;
......@@ -150,8 +125,11 @@ const ContentNotAvailable = styled.div`
const FilterBox = styled.div<{ isActive?: boolean }>`
box-sizing: border-box;
background-color: ${({ theme }) => theme.backgroundInteractive};
font-size: 14px;
font-weight: 600;
line-height: 14px;
color: ${({ theme }) => theme.textPrimary};
padding: 12px 16px;
padding: 8px 16px;
border-radius: 12px;
cursor: pointer;
box-sizing: border-box;
......@@ -383,43 +361,39 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
<AssetView asset={asset} mediaType={assetMediaType} dominantColor={dominantColor} />
)}
</MediaContainer>
<DefaultLink to={`/nfts/collection/${asset.address}`}>
<CollectionHeader>
{collection.collectionName} {collection.isVerified && <VerifiedIcon />}
</CollectionHeader>
</DefaultLink>
<AssetHeader>{asset.name ?? `${asset.collectionName} #${asset.tokenId}`}</AssetHeader>
<AssetPriceDetailsContainer>
<AssetPriceDetails asset={asset} collection={collection} />
</AssetPriceDetailsContainer>
<InfoContainer
primaryHeader="Traits"
defaultOpen
secondaryHeader={
rarityProvider && rarity && rarity.score ? (
<MouseoverTooltip
text={
<HoverContainer>
<HoverImageContainer>
<img src={rarityProviderLogo} alt="cardLogo" width={16} />
</HoverImageContainer>
<ContainerText>
{`Ranking by ${rarity.provider === 'Genie' ? fallbackProvider : rarity.provider}`}
</ContainerText>
</HoverContainer>
}
placement="top"
>
<RarityWrap>Rarity: {putCommas(rarity.score)}</RarityWrap>
</MouseoverTooltip>
) : null
}
>
<TraitsContainer asset={asset} />
</InfoContainer>
{asset.traits && (
<InfoContainer
primaryHeader="Traits"
defaultOpen
secondaryHeader={
rarityProvider && rarity && rarity.score ? (
<MouseoverTooltip
text={
<HoverContainer>
<HoverImageContainer>
<img src={rarityProviderLogo} alt="cardLogo" width={16} />
</HoverImageContainer>
<ContainerText>
{`Ranking by ${rarity.provider === 'Genie' ? fallbackProvider : rarity.provider}`}
</ContainerText>
</HoverContainer>
}
placement="top"
>
<RarityWrap>Rarity: {putCommas(rarity.score)}</RarityWrap>
</MouseoverTooltip>
) : null
}
>
<TraitsContainer asset={asset} />
</InfoContainer>
)}
<InfoContainer
primaryHeader="Activity"
defaultOpen
secondaryHeader={formattedPrice ? `Last Sale: ${formattedPrice} ETH` : undefined}
>
<>
......
import { useWeb3React } from '@web3-react/core'
import { OpacityHoverState } from 'components/Common'
import { useNftBalanceQuery } from 'graphql/data/nft/NftBalance'
import useCopyClipboard from 'hooks/useCopyClipboard'
import { CancelListingIcon } from 'nft/components/icons'
import { CancelListingIcon, VerifiedIcon } from 'nft/components/icons'
import { useBag, useProfilePageState, useSellAsset } from 'nft/hooks'
import { CollectionInfoForAsset, GenieAsset, ProfilePageStateType, WalletAsset } from 'nft/types'
import { ethNumberStandardFormatter, formatEthPrice, getMarketplaceIcon, timeLeft, useUsdPrice } from 'nft/utils'
import {
ethNumberStandardFormatter,
formatEthPrice,
generateTweetForAsset,
getMarketplaceIcon,
timeLeft,
useUsdPrice,
} from 'nft/utils'
import { shortenAddress } from 'nft/utils/address'
import { useMemo } from 'react'
import { Upload } from 'react-feather'
......@@ -13,6 +19,9 @@ import { Link, useNavigate } from 'react-router-dom'
import styled, { css, useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
const TWITTER_WIDTH = 560
const TWITTER_HEIGHT = 480
interface AssetPriceDetailsProps {
asset: GenieAsset
collection: CollectionInfoForAsset
......@@ -45,12 +54,15 @@ const hoverState = css`
`
const Container = styled.div`
display: flex;
flex-direction: column;
width: 100%;
gap: 24px;
@media (min-width: 960px) {
@media (min-width: calc(960px + 1px)) {
position: fixed;
width: 360px;
margin-top: -6px;
margin-top: 20px;
}
`
......@@ -129,7 +141,8 @@ const DiscoveryContainer = styled.div`
`
const OwnerText = styled.a`
font-size: 14px;
font-size: 16px;
font-weight: 600;
line-height: 20px;
color: ${({ theme }) => theme.textSecondary};
text-decoration: none;
......@@ -140,9 +153,48 @@ const OwnerText = styled.a`
const OwnerInformationContainer = styled.div`
color: ${({ theme }) => theme.textSecondary};
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 0 8px;
margin-bottom: 20px;
`
const AssetInfoContainer = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`
const AssetHeader = styled.div`
display: -webkit-box;
align-items: center;
font-size: 28px;
font-weight: 500;
line-height: 36px;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
color: ${({ theme }) => theme.textPrimary};
`
const CollectionNameContainer = styled.div`
display: flex;
justify-content: space-between;
`
const CollectionHeader = styled.span`
display: flex;
align-items: center;
font-size: 16px;
font-weight: 400;
line-height: 24px;
color: ${({ theme }) => theme.textPrimary};
text-decoration: none;
${OpacityHoverState};
`
const DefaultLink = styled(Link)`
text-decoration: none;
`
export const OwnerContainer = ({ asset }: { asset: GenieAsset }) => {
......@@ -168,48 +220,46 @@ export const OwnerContainer = ({ asset }: { asset: GenieAsset }) => {
}
return (
<Container>
<BestPriceContainer>
<HeaderRow>
<ThemedText.SubHeader fontWeight={500} lineHeight="24px">
{listing ? 'Your Price' : 'List for Sale'}
</ThemedText.SubHeader>
{listing && <MarketplaceIcon alt={listing.marketplace} src={getMarketplaceIcon(listing.marketplace)} />}
</HeaderRow>
<PriceRow>
{listing ? (
<>
<ThemedText.MediumHeader fontSize="28px" lineHeight="36px">
{formatEthPrice(asset.priceInfo.ETHPrice)}
</ThemedText.MediumHeader>
{USDPrice && (
<ThemedText.BodySecondary lineHeight="24px">
{ethNumberStandardFormatter(USDPrice, true, true)}
</ThemedText.BodySecondary>
)}
</>
) : (
<ThemedText.BodySecondary fontSize="14px" lineHeight="20px">
Get the best price for your NFT by selling with Uniswap.
</ThemedText.BodySecondary>
)}
</PriceRow>
{expirationDate && (
<ThemedText.BodySecondary fontSize="14px">Sale ends: {timeLeft(expirationDate)}</ThemedText.BodySecondary>
)}
{!listing ? (
<BuyNowButton assetInBag={false} margin={true} useAccentColor={true} onClick={goToListPage}>
<ThemedText.SubHeader lineHeight="20px">List</ThemedText.SubHeader>
</BuyNowButton>
) : (
<BestPriceContainer>
<HeaderRow>
<ThemedText.SubHeader color="accentAction" fontWeight={500} lineHeight="24px">
{listing ? 'Your Price' : 'List for Sale'}
</ThemedText.SubHeader>
{listing && <MarketplaceIcon alt={listing.marketplace} src={getMarketplaceIcon(listing.marketplace)} />}
</HeaderRow>
<PriceRow>
{listing ? (
<>
<BuyNowButton assetInBag={false} margin={true} useAccentColor={false} onClick={goToListPage}>
<ThemedText.SubHeader lineHeight="20px">Adjust listing</ThemedText.SubHeader>
</BuyNowButton>
<ThemedText.MediumHeader fontSize="28px" lineHeight="36px">
{formatEthPrice(asset.priceInfo.ETHPrice)}
</ThemedText.MediumHeader>
{USDPrice && (
<ThemedText.BodySecondary lineHeight="24px">
{ethNumberStandardFormatter(USDPrice, true, true)}
</ThemedText.BodySecondary>
)}
</>
) : (
<ThemedText.BodySecondary fontSize="14px" lineHeight="20px">
Get the best price for your NFT by selling with Uniswap.
</ThemedText.BodySecondary>
)}
</BestPriceContainer>
</Container>
</PriceRow>
{expirationDate && (
<ThemedText.BodySecondary fontSize="14px">Sale ends: {timeLeft(expirationDate)}</ThemedText.BodySecondary>
)}
{!listing ? (
<BuyNowButton assetInBag={false} margin={true} useAccentColor={true} onClick={goToListPage}>
<ThemedText.SubHeader lineHeight="20px">List</ThemedText.SubHeader>
</BuyNowButton>
) : (
<>
<BuyNowButton assetInBag={false} margin={true} useAccentColor={false} onClick={goToListPage}>
<ThemedText.SubHeader lineHeight="20px">Adjust listing</ThemedText.SubHeader>
</BuyNowButton>
</>
)}
</BestPriceContainer>
)
}
......@@ -258,7 +308,6 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
const bagExpanded = useBag((s) => s.bagExpanded)
const USDPrice = useUsdPrice(asset)
const [, setCopied] = useCopyClipboard()
const { assetInBag } = useMemo(() => {
return {
......@@ -268,40 +317,40 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
}
}, [asset, itemsInBag])
const isOwner = asset.owner ? account?.toLowerCase() === asset.owner?.address?.toLowerCase() : false
if (isOwner) {
return <OwnerContainer asset={asset} />
const shareTweet = () => {
window.open(
generateTweetForAsset(asset),
'newwindow',
`left=${(window.screen.width - TWITTER_WIDTH) / 2}, top=${
(window.screen.height - TWITTER_HEIGHT) / 2
}, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}`
)
}
const isOwner = asset.owner ? account?.toLowerCase() === asset.owner?.address?.toLowerCase() : false
const isForSale = cheapestOrder && asset.priceInfo
return (
<Container>
<OwnerInformationContainer>
<OwnerText
target="_blank"
href={`https://etherscan.io/address/${asset.owner.address}`}
rel="noopener noreferrer"
>
{asset.tokenType === 'ERC1155' ? (
''
) : (
<span> Seller: {isOwner ? 'you' : asset.owner.address && shortenAddress(asset.owner.address, 2, 4)}</span>
)}
</OwnerText>
<UploadLink
onClick={() => {
setCopied(window.location.href)
}}
target="_blank"
>
<Upload size={20} strokeWidth={2} />
</UploadLink>
</OwnerInformationContainer>
{cheapestOrder && asset.priceInfo ? (
<AssetInfoContainer>
<CollectionNameContainer>
<DefaultLink to={`/nfts/collection/${asset.address}`}>
<CollectionHeader>
{collection.collectionName} {collection.isVerified && <VerifiedIcon />}
</CollectionHeader>
</DefaultLink>
<UploadLink onClick={shareTweet} target="_blank">
<Upload size={20} strokeWidth={2} />
</UploadLink>
</CollectionNameContainer>
<AssetHeader>{asset.name ?? `${asset.collectionName} #${asset.tokenId}`}</AssetHeader>
</AssetInfoContainer>
{isOwner ? (
<OwnerContainer asset={asset} />
) : isForSale ? (
<BestPriceContainer>
<HeaderRow>
<ThemedText.SubHeader fontWeight={500} lineHeight="24px">
<ThemedText.SubHeader color="accentAction" fontWeight={500} lineHeight="24px">
Best Price
</ThemedText.SubHeader>
<MarketplaceIcon alt={cheapestOrder.marketplace} src={getMarketplaceIcon(cheapestOrder.marketplace)} />
......@@ -342,6 +391,24 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
) : (
<NotForSale collectionName={collection.collectionName ?? 'this collection'} collectionUrl={asset.address} />
)}
{isForSale && (
<OwnerInformationContainer>
<ThemedText.BodySmall color="textSecondary" lineHeight="20px">
Seller:
</ThemedText.BodySmall>
<OwnerText
target="_blank"
href={`https://etherscan.io/address/${asset.owner.address}`}
rel="noopener noreferrer"
>
{asset.tokenType === 'ERC1155' ? (
''
) : (
<span> {isOwner ? 'You' : asset.owner.address && shortenAddress(asset.owner.address, 2, 4)}</span>
)}
</OwnerText>
</OwnerInformationContainer>
)}
</Container>
)
}
......@@ -11,7 +11,7 @@ const Grid = styled.div`
gap: 16px;
max-width: 780px;
@media (max-width: 960px) {
@media (max-width: 1080px) {
grid-template-columns: 1fr 1fr 1fr;
}
......
......@@ -28,3 +28,11 @@ export const getAssetHref = (asset: GenieAsset | WalletAsset, origin?: DetailsOr
export const getMarketplaceIcon = (marketplace: string) => {
return `/nft/svgs/marketplaces/${marketplace.toLowerCase()}.svg`
}
export const generateTweetForAsset = (asset: GenieAsset): string => {
return `https://twitter.com/intent/tweet?text=Check%20out%20${
asset.name ?? `${asset.collectionName}%20%23${asset.tokenId}`
}%20(${asset.collectionName})%20https://app.uniswap.org/%23/nfts/asset/${asset.address}/${
asset.tokenId
}%20via%20@uniswap`
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment