Skip to content

Commit

Permalink
feat: markets category route (#7794)
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre authored Sep 26, 2024
1 parent 9c73047 commit 4cc9a6a
Show file tree
Hide file tree
Showing 18 changed files with 829 additions and 471 deletions.
7 changes: 6 additions & 1 deletion src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -2597,8 +2597,13 @@
"markets": {
"recommended": "Recommended",
"watchlist": "My Watchlist",
"categoriesTabTitle": "Categories",
"marketsBody": "Nunc vel efficitur ligula, vel sagittis est. Morbi et sapien a ligula venenatis consequat.",
"watchlistEmpty": {
"emptyTitle": "No assets in Watchlist",
"emptyBody": "It appears you haven't starred any assets just yet. Start adding assets to your watchlist now to track them!"
},
"emptyTitle": "No assets found",
"emptyBody": "No assets were found with the current filter. Try adjusting your selected chain to see more assets.",
"categories": {
"oneClickDefiAssets": {
"title": "One Click DeFi Assets"
Expand Down
1 change: 1 addition & 0 deletions src/components/AssetHeader/AssetHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const AssetHeader: React.FC<AssetHeaderProps> = ({ assetId, accountId })
{name} {`(${symbol}${asset.id ? ` ${middleEllipsis(asset.id)}` : ''})`}
</Heading>

<WatchAssetButton assetId={assetId} />
<IconButton
as={Link}
isExternal
Expand Down
50 changes: 30 additions & 20 deletions src/components/AssetHeader/WatchAssetButton.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
import { IconButton } from '@chakra-ui/react'
import type { BoxProps } from '@chakra-ui/react'
import { Box, Icon } from '@chakra-ui/react'
import type { AssetId } from '@shapeshiftoss/caip'
import { useCallback } from 'react'
import { FaRegStar, FaStar } from 'react-icons/fa'
import { preferences } from 'state/slices/preferencesSlice/preferencesSlice'
import { selectIsAssetIdWatched } from 'state/slices/selectors'
import { useAppDispatch, useAppSelector } from 'state/store'

const starEmpty = <FaRegStar />
const starFilled = <FaStar />

type WatchAssetButtonProps = {
type WatchAssetButtonProps = Partial<BoxProps> & {
assetId: AssetId
}

export const WatchAssetButton: React.FC<WatchAssetButtonProps> = ({ assetId }) => {
export const WatchAssetButton: React.FC<WatchAssetButtonProps> = ({ assetId, ...props }) => {
const appDispatch = useAppDispatch()
const isAssetIdWatched = useAppSelector(state => selectIsAssetIdWatched(state, assetId))
const handleToggleWatchAsset = useCallback(() => {
if (isAssetIdWatched) {
appDispatch(preferences.actions.removeWatchedAssetId(assetId))
} else {
appDispatch(preferences.actions.addWatchedAssetId(assetId))
}
}, [appDispatch, assetId, isAssetIdWatched])

const handleToggleWatchAsset: React.MouseEventHandler<HTMLButtonElement> = useCallback(
e => {
e.stopPropagation()
appDispatch(preferences.actions.toggleWatchedAssetId(assetId))
},
[appDispatch, assetId],
)

return (
<IconButton
onClick={handleToggleWatchAsset}
icon={isAssetIdWatched ? starFilled : starEmpty}
<Box
as='span'
display='inline-flex'
justifyContent='center'
alignItems='center'
minWidth='auto'
borderRadius='full'
bg='var(--chakra-colors-background-button-secondary-base)'
// eslint-disable-next-line react-memo/require-usememo
_hover={{ bg: 'var(--chakra-colors-background-button-secondary-hover)' }}
p={2}
ml={2}
aria-label='favorite asset'
isRound
variant='ghost'
fontSize='2xl'
/>
onClick={handleToggleWatchAsset}
{...props}
>
<Icon as={isAssetIdWatched ? FaStar : FaRegStar} />
</Box>
)
}
47 changes: 47 additions & 0 deletions src/pages/Markets/Category.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Box } from '@chakra-ui/react'
import { useMemo } from 'react'
import { useTranslate } from 'react-polyglot'
import { useParams } from 'react-router'
import { Main } from 'components/Layout/Main'
import { SEO } from 'components/Layout/Seo'

import { MarketsRow } from './components/MarketsRow'
import type { MARKETS_CATEGORIES } from './constants'
import { useRows } from './hooks/useRows'
import { MarketsHeader } from './MarketsHeader'

const containerPaddingX = { base: 4, xl: 0 }

const ASSETS_LIMIT = 100

export const Category: React.FC = () => {
const params = useParams<{ category: MARKETS_CATEGORIES }>()
const translate = useTranslate()
const headerComponent = useMemo(() => <MarketsHeader />, [])

const allRows = useRows({ limit: ASSETS_LIMIT })
const row = allRows[params.category]

const body = useMemo(
() => (
<MarketsRow
title={row.title}
subtitle={row.subtitle}
supportedChainIds={row.supportedChainIds}
category={row.category}
>
{row.component}
</MarketsRow>
),
[row.category, row.component, row.subtitle, row.supportedChainIds, row.title],
)

return (
<Main headerComponent={headerComponent} isSubPage>
<SEO title={translate('navBar.markets')} />
<Box py={4} px={containerPaddingX}>
{body}
</Box>
</Main>
)
}
39 changes: 25 additions & 14 deletions src/pages/Markets/MarketsHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,19 @@ export const MarketsHeader = () => {
path: '/markets/watchlist',
color: 'blue',
},
{
label: 'markets.categoriesTabTitle',
path: '/markets/categories',
color: 'blue',
},
]
}, [])

const handleBack = useCallback(() => {
history.push('/explore')
}, [history])

const maybeCategory = useMemo(
() =>
history.location.pathname.match(/\/markets\/category\/(?<category>[\w]+)/)?.groups?.category,
[history.location.pathname],
)

return (
<>
<Display.Mobile>
Expand All @@ -51,15 +52,25 @@ export const MarketsHeader = () => {
</PageHeader>
</Display.Mobile>
<Stack mb={4}>
<Container maxWidth='container.4xl' px={containerPadding} pt={containerPaddingTop} pb={4}>
<Display.Desktop>
<Stack>
<Heading>{translate('navBar.markets')}</Heading>
<Text color='text.subtle' translation='markets.marketsBody' />
</Stack>
</Display.Desktop>
</Container>
<TabMenu items={NavItems} />
{!maybeCategory && (
// Don't show tabs and heading when on a single category view
<>
<Container
maxWidth='container.4xl'
px={containerPadding}
pt={containerPaddingTop}
pb={4}
>
<Display.Desktop>
<Stack>
<Heading>{translate('navBar.markets')}</Heading>
<Text color='text.subtle' translation='markets.marketsBody' />
</Stack>
</Display.Desktop>
</Container>
<TabMenu items={NavItems} />
</>
)}
</Stack>
</>
)
Expand Down
15 changes: 11 additions & 4 deletions src/pages/Markets/MarketsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { Redirect, Route, Switch, useRouteMatch } from 'react-router'
import { Redirect, Route, Switch, useHistory, useRouteMatch } from 'react-router'

import { Category } from './Category'
import { Recommended } from './Recommended'
import { WatchList } from './Watchlist'

export const MarketsPage = () => {
const { path } = useRouteMatch()
const history = useHistory()

return (
<Switch>
<Switch location={history.location}>
<Route exact path={`${path}`}>
<Redirect to={`${path}/recommended`} />
</Route>
<Route exact path={`${path}/recommended`}>
<Recommended />
</Route>
<Route path={`${path}/watchlist`}></Route>
<Route path={`${path}/categories`}></Route>
<Route exact path={`${path}/watchlist`}>
<WatchList />
</Route>
<Route exact path={`${path}/category/:category`}>
<Category />
</Route>
</Switch>
)
}
Loading

0 comments on commit 4cc9a6a

Please sign in to comment.