Commit ec6c843d authored by Jordan Frankfurt's avatar Jordan Frankfurt Committed by GitHub

fix: collection screen for mobile (#5287)

* fix: collection screen for mobile

* margin to match collection item container

* fix loading skeleton

* pr feedback + some minor code cleanup
parent 7a6bb369
......@@ -29,6 +29,3 @@ export const assetList = style([
},
},
])
//Using negative margin and overflowing the width but 2*16px so that the edges of this area always properly clip the softer, wider shadow on the cards
export const actionBarContainer = style([{ marginLeft: '-16px', width: 'calc(100% + 32px)' }])
......@@ -48,7 +48,7 @@ import { applyFiltersFromURL, syncLocalFiltersWithURL } from 'nft/utils/urlParam
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component'
import { useLocation } from 'react-router-dom'
import styled from 'styled-components/macro'
import styled, { css } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { CollectionAssetLoading } from './CollectionAssetLoading'
......@@ -64,11 +64,26 @@ interface CollectionNftsProps {
const rarityStatusCache = new Map<string, boolean>()
const InfiniteScrollWrapperCss = css`
margin: 0 16px;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.sm}px) {
margin: 0 20px;
}
@media screen and (min-width: ${({ theme }) => theme.breakpoint.md}px) {
margin: 0 26px;
}
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) {
margin: 0 48px;
}
`
const ActionsContainer = styled.div`
display: flex;
flex: 1 1 auto;
gap: 10px;
width: 100%;
justify-content: space-between;
${InfiniteScrollWrapperCss}
`
const ActionsSubContainer = styled.div`
......@@ -93,7 +108,7 @@ export const SortDropdownContainer = styled.div<{ isFiltersExpanded: boolean }>`
const EmptyCollectionWrapper = styled.div`
display: block;
textalign: center;
text-align: center;
`
const ViewFullCollection = styled.span`
......@@ -111,6 +126,10 @@ const ClearAllButton = styled.button`
background: none;
`
const InfiniteScrollWrapper = styled.div`
${InfiniteScrollWrapperCss}
`
const SweepButton = styled.div<{ toggled: boolean; disabled?: boolean }>`
display: flex;
gap: 8px;
......@@ -148,7 +167,7 @@ const MarketNameWrapper = styled(Row)`
gap: 8px;
`
const loadingAssets = () => (
const LoadingAssets = () => (
<>
{Array.from(Array(ASSET_PAGE_SIZE), (_, index) => (
<CollectionAssetLoading key={index} />
......@@ -158,7 +177,7 @@ const loadingAssets = () => (
export const CollectionNftsLoading = () => (
<Box width="full" className={styles.assetList}>
{loadingAssets()}
<LoadingAssets />
</Box>
)
......@@ -278,6 +297,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
}
const { assets: collectionNfts, loadNext, hasNext, isLoadingNext } = useLazyLoadAssetsQuery(assetQueryParams)
const handleNextPageLoad = useCallback(() => loadNext(ASSET_PAGE_SIZE), [loadNext])
const getPoolPosition = useCallback(
(asset: GenieAsset) => {
......@@ -449,6 +469,15 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
}
}, [collectionStats, priceRangeLow, priceRangeHigh, setPriceRangeHigh, setPriceRangeLow])
const handleSweepClick = useCallback(() => {
if (hasErc1155s) return
if (!sweepIsOpen) {
scrollToTop()
if (!bagExpanded && !isMobile) toggleBag()
}
setSweepOpen(!sweepIsOpen)
}, [bagExpanded, hasErc1155s, isMobile, sweepIsOpen, toggleBag])
return (
<>
<AnimatedBox
......@@ -458,8 +487,8 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
width="full"
zIndex="3"
marginBottom={{ sm: '8', md: '20' }}
padding="16"
className={styles.actionBarContainer}
paddingTop="16"
paddingBottom="16"
>
<ActionsContainer>
<ActionsSubContainer>
......@@ -482,26 +511,19 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
</SortDropdownContainer>
<CollectionSearch />
</ActionsSubContainer>
{!hasErc1155s ? (
{!hasErc1155s && (
<SweepButton
toggled={sweepIsOpen}
disabled={hasErc1155s}
className={buttonTextMedium}
onClick={() => {
if (hasErc1155s) return
if (!sweepIsOpen) {
scrollToTop()
if (!bagExpanded && !isMobile) toggleBag()
}
setSweepOpen(!sweepIsOpen)
}}
onClick={handleSweepClick}
>
<SweepIcon viewBox="0 0 24 24" width="20px" height="20px" />
<SweepText fontWeight={600} color="currentColor" lineHeight="20px">
Sweep
</SweepText>
</SweepButton>
) : null}
)}
</ActionsContainer>
{sweepIsOpen && (
<Sweep contractAddress={contractAddress} minPrice={debouncedMinPrice} maxPrice={debouncedMaxPrice} />
......@@ -564,35 +586,37 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
) : null}
</Row>
</AnimatedBox>
<InfiniteScroll
next={() => loadNext(ASSET_PAGE_SIZE)}
hasMore={hasNext}
loader={hasNext && hasNfts ? loadingAssets() : null}
dataLength={collectionAssets?.length ?? 0}
style={{ overflow: 'unset' }}
className={hasNfts || isLoadingNext ? styles.assetList : undefined}
>
{hasNfts ? (
assets
) : collectionAssets?.length === 0 ? (
<Center width="full" color="textSecondary" textAlign="center" style={{ height: '60vh' }}>
<EmptyCollectionWrapper>
<p className={headlineMedium}>No NFTS found</p>
<Box
onClick={reset}
type="button"
className={clsx(bodySmall, buttonTextMedium)}
color="blue"
cursor="pointer"
>
<ViewFullCollection>View full collection</ViewFullCollection>
</Box>
</EmptyCollectionWrapper>
</Center>
) : (
<CollectionNftsLoading />
)}
</InfiniteScroll>
<InfiniteScrollWrapper>
<InfiniteScroll
next={handleNextPageLoad}
hasMore={hasNext}
loader={Boolean(hasNext && hasNfts) && <LoadingAssets />}
dataLength={collectionAssets?.length ?? 0}
style={{ overflow: 'unset' }}
className={hasNfts || isLoadingNext ? styles.assetList : undefined}
>
{hasNfts ? (
assets
) : collectionAssets?.length === 0 ? (
<Center width="full" color="textSecondary" textAlign="center" style={{ height: '60vh' }}>
<EmptyCollectionWrapper>
<p className={headlineMedium}>No NFTS found</p>
<Box
onClick={reset}
type="button"
className={clsx(bodySmall, buttonTextMedium)}
color="blue"
cursor="pointer"
>
<ViewFullCollection>View full collection</ViewFullCollection>
</Box>
</EmptyCollectionWrapper>
</Center>
) : (
<CollectionNftsLoading />
)}
</InfiniteScroll>
</InfiniteScrollWrapper>
</>
)
}
......@@ -2,8 +2,7 @@ import Column from 'components/Column'
import Row from 'components/Row'
import { Box } from 'nft/components/Box'
import { useIsMobile } from 'nft/hooks'
import { CollectionBannerLoading } from 'nft/pages/collection'
import { COLLECTION_BANNER_HEIGHT } from 'nft/pages/collection'
import { BannerWrapper, CollectionBannerLoading } from 'nft/pages/collection'
import { ScreenBreakpointsPaddings } from 'nft/pages/collection/index.css'
import styled from 'styled-components/macro'
......@@ -32,9 +31,9 @@ export const CollectionPageSkeleton = () => {
const isMobile = useIsMobile()
return (
<StyledColumn>
<Box width="full" height={`${COLLECTION_BANNER_HEIGHT}`}>
<BannerWrapper width="full">
<CollectionBannerLoading />
</Box>
</BannerWrapper>
<CollectionDescriptionSection>
<CollectionStatsLoading isMobile={isMobile} />
<StyledRow>{ActivitySwitcherLoading}</StyledRow>
......
......@@ -3,7 +3,6 @@ import { getDeltaArrow } from 'components/Tokens/TokenDetails/PriceChart'
import { Box, BoxProps } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { Marquee } from 'nft/components/layout/Marquee'
import { headlineMedium } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading'
import { GenieCollection, TokenType } from 'nft/types'
......@@ -11,6 +10,7 @@ import { floorFormatter, quantityFormatter, roundWholePercentage, volumeFormatte
import { ReactNode, useEffect, useReducer, useRef, useState } from 'react'
import ReactMarkdown from 'react-markdown'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { DiscordIcon, EllipsisIcon, ExternalIcon, InstagramIcon, TwitterIcon, VerifiedIcon, XMarkIcon } from '../icons'
import * as styles from './CollectionStats.css'
......@@ -134,14 +134,14 @@ const CollectionName = ({
toggleCollectionSocials: () => void
}) => {
const isCollectionStatsLoading = useIsCollectionLoading((state) => state.isCollectionStatsLoading)
const nameClass = isCollectionStatsLoading ? styles.nameTextLoading : clsx(headlineMedium, styles.nameText)
const nameClass = isCollectionStatsLoading ? styles.nameTextLoading : styles.nameText
return (
<Row justifyContent="space-between">
<Row minWidth="0">
<Box marginRight={!isVerified ? '12' : '0'} className={nameClass}>
<ThemedText.HeadlineSmall marginRight={!isVerified ? '12' : '0'} className={nameClass}>
{name}
</Box>
</ThemedText.HeadlineSmall>
{isVerified && <VerifiedIcon style={{ width: '32px', height: '32px' }} />}
<Row
display={{ sm: 'none', md: 'flex' }}
......@@ -246,7 +246,7 @@ const CollectionDescription = ({ description }: { description: string }) => {
const StatsItem = ({ children, label, shouldHide }: { children: ReactNode; label: string; shouldHide: boolean }) => {
return (
<Box display={shouldHide ? 'none' : 'flex'} flexDirection="column" alignItems="baseline" gap="2" height="min">
<span className={styles.statsValue}>{children}</span>
<ThemedText.SubHeader className={styles.statsValue}>{children}</ThemedText.SubHeader>
<Box as="span" className={styles.statsLabel}>
{label}
</Box>
......
......@@ -2,11 +2,6 @@ import { Trace } from '@uniswap/analytics'
import { PageName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { OpacityHoverState } from 'components/Common'
import {
MAX_WIDTH_MEDIA_BREAKPOINT,
MOBILE_MEDIA_BREAKPOINT,
SMALL_MEDIA_BREAKPOINT,
} from 'components/Tokens/constants'
import { useLoadAssetsQuery } from 'graphql/data/nft/Asset'
import { useCollectionQuery, useLoadCollectionQuery } from 'graphql/data/nft/Collection'
import { MobileHoverBag } from 'nft/components/bag/MobileHoverBag'
......@@ -28,7 +23,13 @@ import { TRANSITION_DURATIONS } from 'theme/styles'
const FILTER_WIDTH = 332
const BAG_WIDTH = 324
export const COLLECTION_BANNER_HEIGHT = 288
export const BannerWrapper = styled(Box)`
height: 100px;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.sm}px) {
height: 288px;
}
`
export const CollectionBannerLoading = () => <Box height="full" width="full" className={styles.loadingBanner} />
......@@ -45,25 +46,6 @@ const MobileFilterHeader = styled(Row)`
// As a result it needs 16px padding on either side. These paddings are offset by 16px to account for this. Please see CollectionNFTs.css.ts for the additional sizing context.
// See breakpoint values in ScreenBreakpointsPaddings above - they must match
const CollectionDisplaySection = styled(Row)`
@media screen and (min-width: ${MAX_WIDTH_MEDIA_BREAKPOINT}) {
padding-left: 48px;
padding-right: 48px;
}
@media screen and (max-width: ${MAX_WIDTH_MEDIA_BREAKPOINT}) {
padding-left: 26px;
padding-right: 26px;
}
@media screen and (max-width: ${SMALL_MEDIA_BREAKPOINT}) {
padding-left: 20px;
padding-right: 20px;
}
@media screen and (max-width: ${MOBILE_MEDIA_BREAKPOINT}) {
padding-left: 16px;
padding-right: 16px;
}
align-items: flex-start;
position: relative;
`
......@@ -134,7 +116,7 @@ const Collection = () => {
<Column width="full">
{contractAddress ? (
<>
<Box width="full" height={`${COLLECTION_BANNER_HEIGHT}`}>
<BannerWrapper width="full">
<Box
as={collectionStats?.bannerImageUrl ? 'img' : 'div'}
height="full"
......@@ -147,7 +129,7 @@ const Collection = () => {
className={styles.bannerImage}
background="none"
/>
</Box>
</BannerWrapper>
<CollectionDescriptionSection>
{collectionStats && (
<CollectionStats stats={collectionStats || ({} as GenieCollection)} isMobile={isMobile} />
......
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