import Link from 'next/link' import { EllipsisHorizontalIcon, BuildingLibraryIcon, ArrowTopRightOnSquareIcon, ChevronDownIcon, CurrencyDollarIcon, ChartBarIcon, ArrowsRightLeftIcon, ArrowTrendingUpIcon, MagnifyingGlassIcon, BanknotesIcon, NewspaperIcon, PlusCircleIcon, ArchiveBoxArrowDownIcon, ExclamationTriangleIcon, DocumentTextIcon, Squares2X2Icon, BookOpenIcon, QueueListIcon, LockClosedIcon, ChartPieIcon, } from '@heroicons/react/20/solid' import { useRouter } from 'next/router' import { useTranslation } from 'next-i18next' import React, { Fragment, ReactNode, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react' import { Disclosure, Popover, Transition } from '@headlessui/react' import MangoAccountSummary from './account/MangoAccountSummary' import { HealthType } from '@blockworks-foundation/mango-v4' import { useWallet } from '@solana/wallet-adapter-react' import mangoStore from '@store/mangoStore' import HealthHeart from './account/HealthHeart' import useMangoAccount from 'hooks/useMangoAccount' import { useTheme } from 'next-themes' import LeaderboardIcon from './icons/LeaderboardIcon' import { sideBarAnimationDuration } from './Layout' import { CUSTOM_SKINS, breakpoints } from 'utils/theme' import { NFT } from 'types' import { useViewport } from 'hooks/useViewport' import useLocalStorageState from 'hooks/useLocalStorageState' import { SIDEBAR_COLLAPSE_KEY } from 'utils/constants' import { createTransferInstruction } from '@solana/spl-token' import { PublicKey, TransactionInstruction } from '@solana/web3.js' import CoinIcon from './icons/CoinIcon' import PerpIcon from './icons/PerpIcon' //import { useIsWhiteListed } from 'hooks/useIsWhiteListed' const set = mangoStore.getState().set const SideNav = ({ collapsed }: { collapsed: boolean }) => { const { t } = useTranslation(['common', 'search']) const { connected, publicKey } = useWallet() const { theme } = useTheme() const group = mangoStore.getState().group const activeAccountTab = mangoStore((s) => s.accountPageTab) const themeData = mangoStore((s) => s.themeData) const nfts = mangoStore((s) => s.wallet.nfts.data) const { mangoAccount } = useMangoAccount() //const { data: isWhiteListed } = useIsWhiteListed() const setPrependedGlobalAdditionalInstructions = mangoStore( (s) => s.actions.setPrependedGlobalAdditionalInstructions, ) const router = useRouter() const { pathname, query } = router const { width } = useViewport() const [, setIsCollapsed] = useLocalStorageState(SIDEBAR_COLLAPSE_KEY, false) useEffect(() => { if (width !== 0 && width < breakpoints['xl']) { setIsCollapsed(true) } }, [width, setIsCollapsed]) const playAnimation = () => { const set = mangoStore.getState().set set((s) => { s.successAnimation.theme = true }) } // fetch nfts when pk changes useEffect(() => { if (publicKey) { set((state) => { state.wallet.nfts.initialLoad = true }) const actions = mangoStore.getState().actions const connection = mangoStore.getState().connection actions.fetchNfts(connection, publicKey) } }, [publicKey]) // find all mango skin nfts const mangoNfts = useMemo(() => { if (!nfts.length) return [] const mangoNfts: NFT[] = [] for (const nft of nfts) { const collectionAddress = nft?.collectionAddress for (const themeKey in CUSTOM_SKINS) { if (CUSTOM_SKINS[themeKey] === collectionAddress) { mangoNfts.push(nft) } } } return mangoNfts }, [nfts]) //mark transactions with used nfts useEffect(() => { let newInstruction: TransactionInstruction[] = [] if (mangoNfts.length && theme) { const collectionAddress = CUSTOM_SKINS[theme.toLowerCase()] const usedNft = mangoNfts.find( (nft) => nft.collectionAddress === collectionAddress, ) if (usedNft && publicKey && collectionAddress) { newInstruction = [ createTransferInstruction( new PublicKey(usedNft.tokenAccount), new PublicKey(usedNft.tokenAccount), publicKey, 1, ), ] } } setPrependedGlobalAdditionalInstructions(newInstruction) }, [mangoNfts, theme, themeData]) // find sidebar image url from skin nft for theme const sidebarImageUrl = useMemo(() => { if (!theme) return themeData.sideImagePath const collectionAddress = CUSTOM_SKINS[theme.toLowerCase()] if (collectionAddress && mangoNfts.length) { const attributes = mangoNfts.find( (nft) => nft.collectionAddress === collectionAddress, )?.json?.attributes const sidebarImageUrl = attributes ? attributes[0].value || themeData.sideImagePath : '' return sidebarImageUrl } return themeData.sideImagePath }, [mangoNfts, theme, themeData]) return ( <>
{sidebarImageUrl && !collapsed ? ( playAnimation()} src={sidebarImageUrl} alt="next" /> ) : null}
logo {themeData.platformName}
{/* } title={t('account')} pagePath="/" /> */} } title={t('account')} > } title={t('overview')} pagePath="/?view=overview" hideIconBg /> } title={t('balances')} pagePath="/?view=balances" hideIconBg /> } title={t('trade:positions')} pagePath="/?view=positions" hideIconBg /> } title={t('trade:orders')} pagePath="/?view=orders" hideIconBg /> } title={t('trade:unsettled')} pagePath="/?view=unsettled" hideIconBg /> } title={t('history')} pagePath="/?view=history" hideIconBg /> } title={t('swap')} pagePath="/swap" /> } title={t('trade')} > } title={t('perp')} pagePath="/trade?name=SOL-PERP" hideIconBg /> } title={t('spot')} pagePath="/trade?name=SOL/USDC" hideIconBg /> } title={t('borrow')} pagePath="/borrow" /> } title={t('stats')} pagePath="/stats" /> } title={t('leaderboard')} pagePath="/leaderboard" /> {/* {isWhiteListed ? ( } title={t('nft-market')} pagePath="/nft" /> ) : null} */} } title={t('more')} > } title={t('search:search-accounts')} pagePath="/search" hideIconBg /> } title={t('common:list-market-token')} pagePath="/governance/list" hideIconBg /> } title={t('common:vote')} pagePath="/governance/vote" hideIconBg /> } title={t('documentation')} pagePath="https://docs.mango.markets" hideIconBg isExternal /> } title={t('governance')} pagePath="https://dao.mango.markets" hideIconBg isExternal /> {/* } title={t('feedback-survey')} pagePath="https://forms.gle/JgV4w7SJ2kPH89mq7" hideIconBg isExternal /> */} } title={t('terms-of-use')} pagePath="https://docs.mango.markets/legal" hideIconBg isExternal /> } title={t('risks')} pagePath="https://docs.mango.markets/mango-markets/risks" hideIconBg isExternal />
} panelTitle={mangoAccount?.name ? mangoAccount.name : t('account')} title={

{t('account')}

{mangoAccount ? mangoAccount.name : connected ? 'No Account' : 'Connect'}

} alignBottom hideIconBg >
) } export default SideNav const MenuItem = ({ active, collapsed, icon, onClick, title, pagePath, hideIconBg, isExternal, }: { active?: boolean collapsed: boolean icon?: ReactNode onClick?: () => void title: string pagePath: string hideIconBg?: boolean isExternal?: boolean }) => { const { theme } = useTheme() return (
{icon ? (
{icon}
) : null} {title}
{isExternal ? ( ) : null}
) } export const ExpandableMenuItem = ({ active, alignBottom, children, collapsed, hideIconBg, icon, panelTitle, title, }: { active?: boolean alignBottom?: boolean children: ReactNode collapsed: boolean hideIconBg?: boolean icon: ReactNode panelTitle?: string title: string | ReactNode }) => { const { theme } = useTheme() const themeData = mangoStore((s) => s.themeData) const [isOverButton, setIsOverButton] = useState(false) const [isOverList, setIsOverList] = useState(false) const [isOpen, setIsOpen] = useState(false) const [isTouchInput, setIsTouchInput] = useState(false) const [hasClicked, setHasClicked] = useState(false) const button = useRef(null) useLayoutEffect(() => { if ( isOpen && !isOverButton && !isOverList && !isTouchInput && button?.current ) { button.current.click() setIsOpen(false) } else if ( !isOpen && (isOverButton || isOverList) && !isTouchInput && button?.current ) { button.current.click() setIsOpen(true) } }, [isOverButton, isOverList]) useEffect(() => { setIsTouchInput(false) setHasClicked(false) }, [hasClicked]) // hack for weird bug where isOverList is true when it shouldn't be (only in account nav => click link in menu then overview tab and quickly hover account main menu icon) useEffect(() => { if (isOverButton && isOverList) { setIsOverList(false) } }, [isOverButton, isOverList]) return collapsed ? ( { setIsTouchInput(true) }} onMouseEnter={() => { setIsOverButton(true) }} onMouseLeave={() => { setIsOverButton(false) }} onClick={() => { setHasClicked(true) setIsOpen(!isOpen) }} onKeyDown={() => { setIsOpen(!isOpen) }} title={`${title}`} >
{icon}
{ setIsOverList(true) }} onMouseLeave={() => { setIsOverList(false) }} >
{panelTitle ? (

{panelTitle}

) : null}
setIsOpen(false)}>{children}
) : ( {({ open }) => ( <>
{icon}
{title}
{children}
)}
) }