add market favorites

This commit is contained in:
saml33 2022-11-30 15:01:55 +11:00
parent f32b17c1c8
commit 443858dc36
6 changed files with 201 additions and 48 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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}

View File

@ -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'