Commit 78c8fd23 authored by Jack Short's avatar Jack Short Committed by GitHub

style: updating cards to use appropriate aspect ratio (#5681)

* style: updating cards to use appropriate aspect ratio

* loading card height

* audio and video cards

* change loading height rendering

* respond to self serving comments
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent c3787529
......@@ -16,7 +16,7 @@ import {
import { body, bodySmall, buttonTextMedium, subhead } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useIsMobile } from 'nft/hooks'
import { GenieAsset, Rarity, TokenType, WalletAsset } from 'nft/types'
import { GenieAsset, Rarity, TokenType, UniformAspectRatio, UniformAspectRatios, WalletAsset } from 'nft/types'
import { fallbackProvider, isAudio, isVideo, putCommas } from 'nft/utils'
import { floorFormatter } from 'nft/utils/numbers'
import {
......@@ -240,16 +240,62 @@ const ImageContainer = ({ children, isDisabled = false }: { children: ReactNode;
<StyledImageContainer isDisabled={isDisabled}>{children}</StyledImageContainer>
)
/* -------- CARD IMAGE -------- */
const handleUniformAspectRatio = (
uniformAspectRatio: UniformAspectRatio,
e: React.SyntheticEvent<HTMLElement, Event>,
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void,
renderedHeight?: number,
setRenderedHeight?: (renderedHeight: number | undefined) => void
) => {
if (uniformAspectRatio !== UniformAspectRatios.square && setUniformAspectRatio) {
const height = e.currentTarget.clientHeight
const width = e.currentTarget.clientWidth
const aspectRatio = width / height
if (
(!renderedHeight || renderedHeight !== height) &&
aspectRatio < 1 &&
uniformAspectRatio !== UniformAspectRatios.square &&
setRenderedHeight
) {
setRenderedHeight(height)
}
if (uniformAspectRatio === UniformAspectRatios.unset) {
setUniformAspectRatio(aspectRatio >= 1 ? UniformAspectRatios.square : aspectRatio)
} else if (uniformAspectRatio !== aspectRatio) {
setUniformAspectRatio(UniformAspectRatios.square)
setRenderedHeight && setRenderedHeight(undefined)
}
}
}
function getHeightFromAspectRatio(uniformAspectRatio: UniformAspectRatio, renderedHeight?: number): number | undefined {
return uniformAspectRatio === UniformAspectRatios.square || uniformAspectRatio === UniformAspectRatios.unset
? undefined
: renderedHeight
}
interface ImageProps {
uniformAspectRatio?: UniformAspectRatio
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void
renderedHeight?: number
setRenderedHeight?: (renderedHeight: number | undefined) => void
}
const Image = () => {
const Image = ({
uniformAspectRatio = UniformAspectRatios.square,
setUniformAspectRatio,
renderedHeight,
setRenderedHeight,
}: ImageProps) => {
const { hovered, asset } = useCardContext()
const [noContent, setNoContent] = useState(!asset.smallImageUrl && !asset.imageUrl)
const [loaded, setLoaded] = useState(false)
const isMobile = useIsMobile()
if (noContent) {
return <NoContentContainer />
return <NoContentContainer height={getHeightFromAspectRatio(uniformAspectRatio, renderedHeight)} />
}
return (
......@@ -258,7 +304,7 @@ const Image = () => {
as="img"
width="full"
style={{
aspectRatio: '1',
aspectRatio: `${uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'}`,
transition: 'transform 0.25s ease 0s',
}}
src={asset.imageUrl || asset.smallImageUrl}
......@@ -266,6 +312,7 @@ const Image = () => {
draggable={false}
onError={() => setNoContent(true)}
onLoad={(e) => {
handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight)
setLoaded(true)
}}
className={clsx(hovered && !isMobile && styles.cardImageHover, !loaded && styles.loadingBackground)}
......@@ -274,12 +321,26 @@ const Image = () => {
)
}
function getMediaAspectRatio(
uniformAspectRatio: UniformAspectRatio,
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void
): string {
return uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'
}
interface MediaProps {
shouldPlay: boolean
setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void
}
const Video = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
const Video = ({
uniformAspectRatio = UniformAspectRatios.square,
setUniformAspectRatio,
renderedHeight,
setRenderedHeight,
shouldPlay,
setCurrentTokenPlayingMedia,
}: MediaProps & ImageProps) => {
const vidRef = useRef<HTMLVideoElement>(null)
const { hovered, asset } = useCardContext()
const [noContent, setNoContent] = useState(!asset.smallImageUrl && !asset.imageUrl)
......@@ -293,7 +354,7 @@ const Video = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
}
if (noContent) {
return <NoContentContainer />
return <NoContentContainer height={getHeightFromAspectRatio(uniformAspectRatio, renderedHeight)} />
}
return (
......@@ -304,7 +365,7 @@ const Video = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
alt={asset.name || asset.tokenId}
width="full"
style={{
aspectRatio: '1',
aspectRatio: getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio),
transition: 'transform 0.25s ease 0s',
willChange: 'transform',
}}
......@@ -312,7 +373,8 @@ const Video = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
objectFit="contain"
draggable={false}
onError={() => setNoContent(true)}
onLoad={() => {
onLoad={(e) => {
handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight)
setImageLoaded(true)
}}
visibility={shouldPlay ? 'hidden' : 'visible'}
......@@ -339,7 +401,9 @@ const Video = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
ref={vidRef}
width="full"
style={{
aspectRatio: '1',
aspectRatio: `${
uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'
}`,
}}
onEnded={(e) => {
e.preventDefault()
......@@ -372,7 +436,14 @@ const Video = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
)
}
const Audio = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
const Audio = ({
uniformAspectRatio = UniformAspectRatios.square,
setUniformAspectRatio,
renderedHeight,
setRenderedHeight,
shouldPlay,
setCurrentTokenPlayingMedia,
}: MediaProps & ImageProps) => {
const audRef = useRef<HTMLAudioElement>(null)
const { hovered, asset } = useCardContext()
const [noContent, setNoContent] = useState(!asset.smallImageUrl && !asset.imageUrl)
......@@ -386,7 +457,7 @@ const Audio = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
}
if (noContent) {
return <NoContentContainer />
return <NoContentContainer height={getHeightFromAspectRatio(uniformAspectRatio, renderedHeight)} />
}
return (
......@@ -397,7 +468,7 @@ const Audio = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
alt={asset.name || asset.tokenId}
width="full"
style={{
aspectRatio: '1',
aspectRatio: getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio),
transition: 'transform 0.4s ease 0s',
}}
src={asset.imageUrl || asset.smallImageUrl}
......@@ -405,6 +476,7 @@ const Audio = ({ shouldPlay, setCurrentTokenPlayingMedia }: MediaProps) => {
draggable={false}
onError={() => setNoContent(true)}
onLoad={(e) => {
handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight)
setImageLoaded(true)
}}
className={clsx(hovered && !isMobile && styles.cardImageHover, !imageLoaded && styles.loadingBackground)}
......@@ -729,12 +801,13 @@ const Pool = () => {
)
}
const NoContentContainer = () => (
const NoContentContainer = ({ height }: { height?: number }) => (
<>
<Box
position="relative"
width="full"
style={{
height: height ? `${height}px` : 'auto',
paddingTop: '100%',
background: `linear-gradient(90deg, ${themeVars.colors.backgroundSurface} 0%, ${themeVars.colors.backgroundInteractive} 95.83%)`,
}}
......
......@@ -7,7 +7,7 @@ import Tooltip from 'components/Tooltip'
import { Box } from 'nft/components/Box'
import { bodySmall } from 'nft/css/common.css'
import { useBag } from 'nft/hooks'
import { GenieAsset, isPooledMarket, TokenType } from 'nft/types'
import { GenieAsset, isPooledMarket, TokenType, UniformAspectRatio } from 'nft/types'
import { formatWeiToDecimal, rarityProviderLogo } from 'nft/utils'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import styled from 'styled-components/macro'
......@@ -22,6 +22,10 @@ interface CollectionAssetProps {
mediaShouldBePlaying: boolean
setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void
rarityVerified?: boolean
uniformAspectRatio: UniformAspectRatio
setUniformAspectRatio: (uniformAspectRatio: UniformAspectRatio) => void
renderedHeight?: number
setRenderedHeight: (renderedHeight: number | undefined) => void
}
const TOOLTIP_TIMEOUT = 2000
......@@ -43,6 +47,10 @@ export const CollectionAsset = ({
mediaShouldBePlaying,
setCurrentTokenPlayingMedia,
rarityVerified,
uniformAspectRatio,
setUniformAspectRatio,
renderedHeight,
setRenderedHeight,
}: CollectionAssetProps) => {
const bagManuallyClosed = useBag((state) => state.bagManuallyClosed)
const addAssetsToBag = useBag((state) => state.addAssetsToBag)
......@@ -161,11 +169,30 @@ export const CollectionAsset = ({
timeout={isMobile ? TOOLTIP_TIMEOUT : undefined}
>
{assetMediaType === AssetMediaType.Image ? (
<Card.Image />
<Card.Image
uniformAspectRatio={uniformAspectRatio}
setUniformAspectRatio={setUniformAspectRatio}
renderedHeight={renderedHeight}
setRenderedHeight={setRenderedHeight}
/>
) : assetMediaType === AssetMediaType.Video ? (
<Card.Video shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} />
<Card.Video
shouldPlay={mediaShouldBePlaying}
setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia}
uniformAspectRatio={uniformAspectRatio}
setUniformAspectRatio={setUniformAspectRatio}
renderedHeight={renderedHeight}
setRenderedHeight={setRenderedHeight}
/>
) : (
<Card.Audio shouldPlay={mediaShouldBePlaying} setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} />
<Card.Audio
shouldPlay={mediaShouldBePlaying}
setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia}
uniformAspectRatio={uniformAspectRatio}
setUniformAspectRatio={setUniformAspectRatio}
renderedHeight={renderedHeight}
setRenderedHeight={setRenderedHeight}
/>
)}
</MouseoverTooltip>
</Card.ImageContainer>
......
......@@ -35,7 +35,16 @@ import {
} from 'nft/hooks'
import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading'
import { usePriceRange } from 'nft/hooks/usePriceRange'
import { DropDownOption, GenieAsset, GenieCollection, isPooledMarket, Markets, TokenType } from 'nft/types'
import {
DropDownOption,
GenieAsset,
GenieCollection,
isPooledMarket,
Markets,
TokenType,
UniformAspectRatio,
UniformAspectRatios,
} from 'nft/types'
import {
calcPoolPrice,
calcSudoSwapPrice,
......@@ -169,17 +178,17 @@ const MarketNameWrapper = styled(Row)`
gap: 8px;
`
export const LoadingAssets = ({ count }: { count?: number }) => (
export const LoadingAssets = ({ count, height }: { count?: number; height?: number }) => (
<>
{Array.from(Array(count ?? ASSET_PAGE_SIZE), (_, index) => (
<CollectionAssetLoading key={index} />
<CollectionAssetLoading key={index} height={height} />
))}
</>
)
const CollectionNftsLoading = () => (
const CollectionNftsLoading = ({ height }: { height?: number }) => (
<Box width="full" className={styles.assetList}>
<LoadingAssets />
<LoadingAssets height={height} />
</Box>
)
......@@ -269,6 +278,9 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
const debouncedMaxPrice = useDebounce(maxPrice, 500)
const debouncedSearchByNameText = useDebounce(searchByNameText, 500)
const [uniformAspectRatio, setUniformAspectRatio] = useState<UniformAspectRatio>(UniformAspectRatios.unset)
const [renderedHeight, setRenderedHeight] = useState<number | undefined>()
const [sweepIsOpen, setSweepOpen] = useState(false)
// Load all sweep queries. Loading them on the parent allows lazy-loading, but avoids waterfalling requests.
const collectionParams = useSweepFetcherParams(contractAddress, 'others', debouncedMinPrice, debouncedMaxPrice)
......@@ -413,9 +425,13 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
mediaShouldBePlaying={asset.tokenId === currentTokenPlayingMedia}
setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia}
rarityVerified={rarityVerified}
uniformAspectRatio={uniformAspectRatio}
setUniformAspectRatio={setUniformAspectRatio}
renderedHeight={renderedHeight}
setRenderedHeight={setRenderedHeight}
/>
))
}, [collectionAssets, currentTokenPlayingMedia, isMobile, rarityVerified])
}, [collectionAssets, isMobile, currentTokenPlayingMedia, rarityVerified, uniformAspectRatio, renderedHeight])
const hasNfts = collectionAssets && collectionAssets.length > 0
const hasErc1155s = hasNfts && collectionAssets[0] && collectionAssets[0].tokenType === TokenType.ERC1155
......@@ -461,6 +477,11 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location])
useEffect(() => {
setUniformAspectRatio(UniformAspectRatios.unset)
setRenderedHeight(undefined)
}, [contractAddress])
useEffect(() => {
if (collectionStats && collectionStats.stats?.floor_price) {
const lowValue = collectionStats.stats?.floor_price
......@@ -601,7 +622,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
<InfiniteScroll
next={handleNextPageLoad}
hasMore={hasNext}
loader={Boolean(hasNext && hasNfts) && <LoadingAssets />}
loader={Boolean(hasNext && hasNfts) && <LoadingAssets height={renderedHeight} />}
dataLength={collectionAssets?.length ?? 0}
style={{ overflow: 'unset' }}
className={hasNfts || isLoadingNext ? styles.assetList : undefined}
......@@ -624,7 +645,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
</EmptyCollectionWrapper>
</Center>
) : (
<CollectionNftsLoading />
<CollectionNftsLoading height={renderedHeight} />
)}
</InfiniteScroll>
</InfiniteScrollWrapper>
......
......@@ -36,12 +36,12 @@ export type CollectionSort = Record<
'asc' | 'desc' | 1 | -1 | { $gte?: string | number; $lte?: string | number } | string | number
>
export enum UniformHeights {
export enum UniformAspectRatios {
unset,
notUniform,
square,
}
export type UniformHeight = UniformHeights | number
export type UniformAspectRatio = UniformAspectRatios | number
export enum ActivityEventType {
Listing = 'LISTING',
......
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