Commit a887666b authored by Jack Short's avatar Jack Short Committed by GitHub

feat: mobile hover bag (#4742)

* initial hover bag

* feat: mobile hover bag

* updating mobile bag

* addressing comments and adding stuff to usebag
parent ed8aa082
......@@ -112,6 +112,8 @@ const Bag = () => {
const removeAssetFromBag = useBag((s) => s.removeAssetFromBag)
const bagExpanded = useBag((s) => s.bagExpanded)
const toggleBag = useBag((s) => s.toggleBag)
const setTotalEthPrice = useBag((s) => s.setTotalEthPrice)
const setTotalUsdPrice = useBag((s) => s.setTotalUsdPrice)
const { address, balance: balanceInEth, provider } = useWalletBalance()
const isConnected = !!provider && !!address
......@@ -300,6 +302,11 @@ const Bag = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [transactionStateRef.current])
useEffect(() => {
setTotalEthPrice(totalEthPrice)
setTotalUsdPrice(totalUsdPrice)
}, [totalEthPrice, totalUsdPrice, setTotalEthPrice, setTotalUsdPrice])
const hasAssetsToShow = itemsInBag.length > 0 || unavailableAssets.length > 0
const scrollHandler = (event: React.UIEvent<HTMLDivElement>) => {
......
......@@ -195,3 +195,11 @@ export const toolTip = sprinkles({
display: 'flex',
flexShrink: '0',
})
export const removeAssetOverlay = style([
sprinkles({
position: 'absolute',
right: '4',
top: '4',
}),
])
......@@ -7,6 +7,7 @@ import { Column, Row } from 'nft/components/Flex'
import {
ChevronDownBagIcon,
ChevronUpBagIcon,
CircularCloseIcon,
CloseTimerIcon,
SquareArrowDownIcon,
SquareArrowUpIcon,
......@@ -57,6 +58,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
const [noImageAvailable, setNoImageAvailable] = useState(!asset.smallImageUrl)
const handleCardHover = () => setCardHovered(!cardHovered)
const assetCardRef = useRef<HTMLDivElement>(null)
const showRemoveButton = showRemove && cardHovered
if (cardHovered && assetCardRef.current && assetCardRef.current.matches(':hover') === false) setCardHovered(false)
......@@ -64,6 +66,19 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
<Link to={getAssetHref(asset)} style={{ textDecoration: 'none' }}>
<Row ref={assetCardRef} className={styles.bagRow} onMouseEnter={handleCardHover} onMouseLeave={handleCardHover}>
<Box position="relative" display="flex">
<Box
display={showRemove && isMobile ? 'block' : 'none'}
className={styles.removeAssetOverlay}
onClick={(e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
removeAsset(asset)
}}
transition="250"
zIndex="1"
>
<CircularCloseIcon />
</Box>
{!noImageAvailable && (
<Box
as="img"
......@@ -91,7 +106,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
{asset.collectionIsVerified && <VerifiedIcon className={styles.icon} />}
</Row>
</Column>
{cardHovered && showRemove && (
{showRemoveButton && !isMobile && (
<Box
marginLeft="16"
className={styles.removeBagRowButton}
......@@ -104,7 +119,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
Remove
</Box>
)}
{(!cardHovered || !showRemove) && (
{(!showRemoveButton || isMobile) && (
<Column flexShrink="0">
<Box className={styles.bagRowPrice}>
{`${formatWeiToDecimal(
......
import { style } from '@vanilla-extract/css'
import { buttonTextSmall } from 'nft/css/common.css'
import { sprinkles } from 'nft/css/sprinkles.css'
export const bagContainer = style([
sprinkles({
position: 'fixed',
bottom: '72',
left: '16',
right: '16',
background: 'backgroundModule',
padding: '8',
zIndex: 'fixed',
borderRadius: '8',
justifyContent: 'space-between',
}),
])
export const viewBagButton = style([
buttonTextSmall,
sprinkles({
color: 'explicitWhite',
backgroundColor: 'accentAction',
paddingY: '8',
paddingX: '18',
borderRadius: '12',
cursor: 'pointer',
}),
])
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { body, bodySmall } from 'nft/css/common.css'
import { useBag } from 'nft/hooks'
import { ethNumberStandardFormatter, formatWeiToDecimal, roundAndPluralize } from 'nft/utils'
import * as styles from './MobileHoverBag.css'
export const MobileHoverBag = () => {
const itemsInBag = useBag((state) => state.itemsInBag)
const toggleBag = useBag((state) => state.toggleBag)
const totalEthPrice = useBag((state) => state.totalEthPrice)
const totalUsdPrice = useBag((state) => state.totalUsdPrice)
const shouldShowBag = itemsInBag.length > 0
return (
<Row display={{ sm: shouldShowBag ? 'flex' : 'none', md: 'none' }} className={styles.bagContainer}>
<Row gap="8">
<Box position="relative" style={{ width: '34px', height: '34px' }}>
{itemsInBag.slice(0, 3).map((item, index) => {
return (
<Box
as="img"
key={index}
position="absolute"
src={item.asset.smallImageUrl}
top="1/2"
left="1/2"
width="26"
height="26"
borderRadius="4"
style={{
transform:
index === 0
? 'translate(-50%, -50%) rotate(-4.42deg)'
: index === 1
? 'translate(-50%, -50%) rotate(-14.01deg)'
: 'translate(-50%, -50%) rotate(10.24deg)',
zIndex: index,
}}
/>
)
})}
</Box>
<Column>
<Box className={body} fontWeight="semibold">
{roundAndPluralize(itemsInBag.length, 'item')}
</Box>
<Row gap="8">
<Box className={body}>{`${formatWeiToDecimal(totalEthPrice.toString())}`}</Box>
<Box color="textSecondary" className={bodySmall}>{`${ethNumberStandardFormatter(
totalUsdPrice,
true
)}`}</Box>
</Row>
</Column>
</Row>
<Box className={styles.viewBagButton} onClick={toggleBag}>
View bag
</Box>
</Row>
)
}
import { BigNumber } from '@ethersproject/bignumber'
import { BagItem, BagItemStatus, BagStatus, UpdatedGenieAsset } from 'nft/types'
import { v4 as uuidv4 } from 'uuid'
import create from 'zustand'
......@@ -8,6 +9,10 @@ interface BagState {
setBagStatus: (state: BagStatus) => void
itemsInBag: BagItem[]
setItemsInBag: (items: BagItem[]) => void
totalEthPrice: BigNumber
setTotalEthPrice: (totalEthPrice: BigNumber) => void
totalUsdPrice: number | undefined
setTotalUsdPrice: (totalUsdPrice: number | undefined) => void
addAssetToBag: (asset: UpdatedGenieAsset) => void
removeAssetFromBag: (asset: UpdatedGenieAsset) => void
markAssetAsReviewed: (asset: UpdatedGenieAsset, toKeep: boolean) => void
......@@ -61,6 +66,16 @@ export const useBag = create<BagState>()(
set(() => ({
itemsInBag: items,
})),
totalEthPrice: BigNumber.from(0),
setTotalEthPrice: (totalEthPrice) =>
set(() => ({
totalEthPrice,
})),
totalUsdPrice: undefined,
setTotalUsdPrice: (totalUsdPrice) =>
set(() => ({
totalUsdPrice,
})),
addAssetToBag: (asset) =>
set(({ itemsInBag }) => {
if (get().isLocked) return { itemsInBag: get().itemsInBag }
......
import { MobileHoverBag } from 'nft/components/bag/MobileHoverBag'
import { AnimatedBox, Box } from 'nft/components/Box'
import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
import { Column, Row } from 'nft/components/Flex'
......@@ -59,80 +60,83 @@ const Collection = () => {
}
return (
<Column width="full">
{contractAddress ? (
<>
{' '}
<Box width="full" height="160">
<>
<Column width="full">
{contractAddress ? (
<>
{' '}
<Box width="full" height="160">
{isLoading ? (
<Box height="full" width="full" className={styles.loadingBanner} />
) : (
<Box
as="img"
height="full"
width="full"
src={collectionStats?.bannerImageUrl}
className={isLoading ? styles.loadingBanner : styles.bannerImage}
background="none"
/>
)}
<Box width="full" height="160">
{isLoading ? (
<Box height="full" width="full" className={styles.loadingBanner} />
) : (
<Box
as="img"
height="full"
width="full"
src={collectionStats?.bannerImageUrl}
className={isLoading ? styles.loadingBanner : styles.bannerImage}
background="none"
/>
)}
</Box>
</Box>
</Box>
<Column paddingX="32">
{(isLoading || collectionStats !== undefined) && (
<CollectionStats stats={collectionStats || ({} as GenieCollection)} isMobile={isMobile} />
)}
<ActivitySwitcher
showActivity={isActivityToggled}
toggleActivity={() => {
isFiltersExpanded && setFiltersExpanded(false)
toggleActivity()
}}
/>
</Column>
<Row alignItems="flex-start" position="relative" paddingX="48">
<Box position="sticky" top="72" width="0">
{isFiltersExpanded && (
<Filters
traitsByAmount={collectionStats?.numTraitsByAmount ?? []}
traits={collectionStats?.traits ?? []}
/>
<Column paddingX="32">
{(isLoading || collectionStats !== undefined) && (
<CollectionStats stats={collectionStats || ({} as GenieCollection)} isMobile={isMobile} />
)}
</Box>
{/* @ts-ignore: https://github.com/microsoft/TypeScript/issues/34933 */}
<AnimatedBox
style={{
transform: gridX.interpolate((x) => `translate(${x as number}px)`),
width: gridWidthOffset.interpolate((x) => `calc(100% - ${x as number}px)`),
}}
>
{isActivityToggled
? contractAddress && (
<Activity
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified ?? false}
collectionName={collectionStats?.name ?? ''}
/>
)
: contractAddress &&
(isLoading || collectionStats !== undefined) && (
<CollectionNfts
collectionStats={collectionStats || ({} as GenieCollection)}
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified}
/>
)}
</AnimatedBox>
</Row>
</>
) : (
// TODO: Put no collection asset page here
!isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
)}
</Column>
<ActivitySwitcher
showActivity={isActivityToggled}
toggleActivity={() => {
isFiltersExpanded && setFiltersExpanded(false)
toggleActivity()
}}
/>
</Column>
<Row alignItems="flex-start" position="relative" paddingX="48">
<Box position="sticky" top="72" width="0">
{isFiltersExpanded && (
<Filters
traitsByAmount={collectionStats?.numTraitsByAmount ?? []}
traits={collectionStats?.traits ?? []}
/>
)}
</Box>
{/* @ts-ignore: https://github.com/microsoft/TypeScript/issues/34933 */}
<AnimatedBox
style={{
transform: gridX.interpolate((x) => `translate(${x as number}px)`),
width: gridWidthOffset.interpolate((x) => `calc(100% - ${x as number}px)`),
}}
>
{isActivityToggled
? contractAddress && (
<Activity
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified ?? false}
collectionName={collectionStats?.name ?? ''}
/>
)
: contractAddress &&
(isLoading || collectionStats !== undefined) && (
<CollectionNfts
collectionStats={collectionStats || ({} as GenieCollection)}
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified}
/>
)}
</AnimatedBox>
</Row>
</>
) : (
// TODO: Put no collection asset page here
!isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
)}
</Column>
<MobileHoverBag />
</>
)
}
......
......@@ -9,5 +9,6 @@ export * from './isVideo'
export * from './listNfts'
export * from './putCommas'
export * from './rarity'
export * from './roundAndPluralize'
export * from './transactionResponse'
export * from './updatedAssets'
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