add market favorites
This commit is contained in:
parent
f32b17c1c8
commit
443858dc36
|
@ -0,0 +1,45 @@
|
|||
import { StarIcon } from '@heroicons/react/24/outline'
|
||||
import { StarIcon as FilledStarIcon } from '@heroicons/react/20/solid'
|
||||
import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { FAVORITE_MARKETS_KEY } from 'utils/constants'
|
||||
|
||||
const FavoriteMarketButton = ({
|
||||
market,
|
||||
}: {
|
||||
market: PerpMarket | Serum3Market
|
||||
}) => {
|
||||
const [favoriteMarkets, setFavoriteMarkets] = useLocalStorageState(
|
||||
FAVORITE_MARKETS_KEY,
|
||||
[]
|
||||
)
|
||||
|
||||
const addToFavorites = (marketName: string) => {
|
||||
const newFavorites: any = [...favoriteMarkets, marketName]
|
||||
setFavoriteMarkets(newFavorites)
|
||||
}
|
||||
|
||||
const removeFromFavorites = (marketName: string) => {
|
||||
setFavoriteMarkets(favoriteMarkets.filter((m: string) => m !== marketName))
|
||||
}
|
||||
|
||||
return favoriteMarkets.find(
|
||||
(marketName: string) => marketName === market.name
|
||||
) ? (
|
||||
<button
|
||||
className="default-transition flex items-center justify-center text-th-primary md:hover:text-th-fgd-3"
|
||||
onClick={() => removeFromFavorites(market.name)}
|
||||
>
|
||||
<FilledStarIcon className="h-5 w-5" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="default-transition flex items-center justify-center text-th-fgd-4 md:hover:text-th-primary"
|
||||
onClick={() => addToFavorites(market.name)}
|
||||
>
|
||||
<StarIcon className="h-5 w-5" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default FavoriteMarketButton
|
|
@ -1,4 +1,5 @@
|
|||
import Change from '@components/shared/Change'
|
||||
import FavoriteMarketButton from '@components/shared/FavoriteMarketButton'
|
||||
import TabUnderline from '@components/shared/TabUnderline'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
|
@ -39,7 +40,7 @@ const MarketSelectDropdown = () => {
|
|||
} mt-0.5 ml-2 h-6 w-6 flex-shrink-0 text-th-fgd-3`}
|
||||
/>
|
||||
</Popover.Button>
|
||||
<Popover.Panel className="absolute -left-5 top-[46px] z-50 mr-4 w-screen bg-th-bkg-2 pb-2 pt-4 sm:w-56 md:top-[37px]">
|
||||
<Popover.Panel className="absolute -left-5 top-[46px] z-50 mr-4 w-screen bg-th-bkg-2 pb-2 pt-4 sm:w-72 md:top-[37px]">
|
||||
<TabUnderline
|
||||
activeValue={activeTab}
|
||||
onChange={(v) => setActiveTab(v)}
|
||||
|
@ -50,27 +51,32 @@ const MarketSelectDropdown = () => {
|
|||
? serumMarkets?.length
|
||||
? serumMarkets.map((m) => {
|
||||
return (
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/trade',
|
||||
query: { name: m.name },
|
||||
}}
|
||||
<div
|
||||
className="flex items-center justify-between py-2 px-4"
|
||||
key={m.publicKey.toString()}
|
||||
shallow={true}
|
||||
>
|
||||
<div className="default-transition flex items-center py-2 px-4 hover:cursor-pointer hover:bg-th-bkg-2">
|
||||
<MarketLogos market={m} />
|
||||
<span
|
||||
className={
|
||||
m.name === selectedMarket?.name
|
||||
? 'text-th-primary'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{m.name}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/trade',
|
||||
query: { name: m.name },
|
||||
}}
|
||||
shallow={true}
|
||||
>
|
||||
<div className="default-transition flex items-center hover:cursor-pointer hover:text-th-primary">
|
||||
<MarketLogos market={m} />
|
||||
<span
|
||||
className={
|
||||
m.name === selectedMarket?.name
|
||||
? 'text-th-primary'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{m.name}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<FavoriteMarketButton market={m} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
: null
|
||||
|
@ -79,27 +85,32 @@ const MarketSelectDropdown = () => {
|
|||
? perpMarkets?.length
|
||||
? perpMarkets.map((m) => {
|
||||
return (
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/trade',
|
||||
query: { name: m.name },
|
||||
}}
|
||||
<div
|
||||
className="flex items-center justify-between py-2 px-4"
|
||||
key={m.publicKey.toString()}
|
||||
shallow={true}
|
||||
>
|
||||
<div className="default-transition flex items-center py-2 px-4 hover:cursor-pointer hover:bg-th-bkg-2">
|
||||
<MarketLogos market={m} />
|
||||
<span
|
||||
className={
|
||||
m.name === selectedMarket?.name
|
||||
? 'text-th-primary'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{m.name}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/trade',
|
||||
query: { name: m.name },
|
||||
}}
|
||||
shallow={true}
|
||||
>
|
||||
<div className="default-transition flex items-center hover:cursor-pointer hover:bg-th-bkg-2">
|
||||
<MarketLogos market={m} />
|
||||
<span
|
||||
className={
|
||||
m.name === selectedMarket?.name
|
||||
? 'text-th-primary'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{m.name}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<FavoriteMarketButton market={m} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
: null
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { Transition } from '@headlessui/react'
|
||||
import { StarIcon } from '@heroicons/react/20/solid'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FAVORITE_MARKETS_KEY } from 'utils/constants'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import MarketLogos from './MarketLogos'
|
||||
|
||||
const FavoriteMarketsBar = () => {
|
||||
const [favoriteMarkets] = useLocalStorageState(FAVORITE_MARKETS_KEY, [])
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
const { asPath } = useRouter()
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { group } = useMangoGroup()
|
||||
|
||||
return !isMobile ? (
|
||||
<Transition
|
||||
className="flex items-center space-x-4 overflow-hidden border-b border-th-bkg-3 px-6"
|
||||
show={!!favoriteMarkets.length}
|
||||
enter="transition-all ease-in duration-200"
|
||||
enterFrom="opacity-0 h-0"
|
||||
enterTo="opacity-100 h-8"
|
||||
leave="transition-all ease-out duration-200"
|
||||
leaveFrom="opacity-100 h-8"
|
||||
leaveTo="opacity-0 h-0"
|
||||
>
|
||||
<StarIcon className="h-4 w-4 text-th-fgd-4" />
|
||||
{favoriteMarkets.map((mkt: string) => {
|
||||
// const change24h = marketsInfo?.find((m) => m.name === mkt)?.change24h
|
||||
const isPerp = mkt.includes('PERP')
|
||||
let market
|
||||
if (isPerp) {
|
||||
market = group?.getPerpMarketByName(mkt)
|
||||
} else {
|
||||
market = group?.getSerum3MarketByName(mkt)
|
||||
}
|
||||
return (
|
||||
<Link href={`/trade?name=${mkt}`} key={mkt} shallow={true}>
|
||||
<div
|
||||
className={`default-transition flex items-center whitespace-nowrap py-1 text-xs hover:text-th-primary hover:opacity-100 ${
|
||||
asPath.includes(mkt) ||
|
||||
(asPath === '/trade' &&
|
||||
selectedMarket &&
|
||||
selectedMarket.name === mkt)
|
||||
? 'text-th-primary'
|
||||
: 'text-th-fgd-1 opacity-60'
|
||||
}`}
|
||||
>
|
||||
{market ? <MarketLogos market={market} small /> : null}
|
||||
<span className="mb-0 mr-1.5 text-xs">{mkt}</span>
|
||||
{/* {change24h ? (
|
||||
<div
|
||||
className={`text-xs ${
|
||||
change24h
|
||||
? change24h >= 0
|
||||
? 'text-th-green'
|
||||
: 'text-th-red'
|
||||
: 'text-th-fgd-4'
|
||||
}`}
|
||||
>
|
||||
{`${(change24h * 100).toFixed(1)}%`}
|
||||
</div>
|
||||
) : null} */}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</Transition>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default FavoriteMarketsBar
|
|
@ -5,7 +5,13 @@ import Image from 'next/legacy/image'
|
|||
import { useMemo } from 'react'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
|
||||
const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
||||
const MarketLogos = ({
|
||||
market,
|
||||
small,
|
||||
}: {
|
||||
market: Serum3Market | PerpMarket
|
||||
small?: boolean
|
||||
}) => {
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
|
||||
|
@ -39,8 +45,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={`relative mr-1.5 h-5 ${
|
||||
market instanceof Serum3Market ? 'w-[34px]' : 'w-[20px]'
|
||||
className={`relative mr-1.5 ${small ? 'h-4' : 'h-5'} ${
|
||||
market instanceof Serum3Market
|
||||
? small
|
||||
? 'w-[27px]'
|
||||
: 'w-[34px]'
|
||||
: small
|
||||
? 'w-[16px]'
|
||||
: 'w-[20px]'
|
||||
}`}
|
||||
>
|
||||
<div className="absolute left-0 top-0">
|
||||
|
@ -48,12 +60,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
|||
<Image
|
||||
alt=""
|
||||
className="z-10 rounded-full drop-shadow-md"
|
||||
width="20"
|
||||
height="20"
|
||||
width={small ? '16' : '20'}
|
||||
height={small ? '16' : '20'}
|
||||
src={logos.baseLogoURI}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-5 w-5 text-th-fgd-3" />
|
||||
<QuestionMarkCircleIcon
|
||||
className={`${small ? 'h-4 w-4' : 'h-5 w-5'} text-th-fgd-3`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute right-0 top-0">
|
||||
|
@ -61,12 +75,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
|||
<Image
|
||||
alt=""
|
||||
className="rounded-full opacity-60"
|
||||
width="20"
|
||||
height="20"
|
||||
width={small ? '16' : '20'}
|
||||
height={small ? '16' : '20'}
|
||||
src={logos.quoteLogoURI}
|
||||
/>
|
||||
) : market instanceof PerpMarket ? null : (
|
||||
<QuestionMarkCircleIcon className="h-5 w-5 text-th-fgd-3" />
|
||||
<QuestionMarkCircleIcon
|
||||
className={`${small ? 'h-4 w-4' : 'h-5 w-5'} text-th-fgd-3`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -14,6 +14,7 @@ import MobileTradeAdvancedPage from './MobileTradeAdvancedPage'
|
|||
import OrderbookAndTrades from './OrderbookAndTrades'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import TradeOnboardingTour from '@components/tours/TradeOnboardingTour'
|
||||
import FavoriteMarketsBar from './FavoriteMarketsBar'
|
||||
|
||||
const TradingViewChart = dynamic(() => import('./TradingViewChart'), {
|
||||
ssr: false,
|
||||
|
@ -149,6 +150,7 @@ const TradeAdvancedPage = () => {
|
|||
<MobileTradeAdvancedPage />
|
||||
) : (
|
||||
<>
|
||||
<FavoriteMarketsBar />
|
||||
<ResponsiveGridLayout
|
||||
// layouts={savedLayouts ? savedLayouts : defaultLayouts}
|
||||
layouts={defaultLayouts}
|
||||
|
|
|
@ -48,3 +48,5 @@ export const GRID_LAYOUT_KEY = 'savedLayouts-0.1'
|
|||
export const NOTIFICATION_POSITION_KEY = 'notificationPosition'
|
||||
|
||||
export const MIN_SOL_BALANCE = 0.04
|
||||
|
||||
export const FAVORITE_MARKETS_KEY = 'favoriteMarkets'
|
||||
|
|
Loading…
Reference in New Issue