market selection on mobile
This commit is contained in:
parent
0571c2fa00
commit
db991c4509
|
@ -91,7 +91,7 @@ const MarketNavItem: FunctionComponent<MarketNavItemProps> = ({
|
|||
</div>
|
||||
) : null}
|
||||
</button>
|
||||
<div className="ml-1">
|
||||
<div className="ml-1 hidden sm:block">
|
||||
<FavoriteMarketButton market={market} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,14 +10,16 @@ import MobileTableHeader from './mobile/MobileTableHeader'
|
|||
import { ExpandableRow } from './TableElements'
|
||||
import { FavoriteMarketButton } from './TradeNavMenu'
|
||||
import { useSortableData } from '../hooks/useSortableData'
|
||||
import { LinkButton } from './Button'
|
||||
import Button, { LinkButton } from './Button'
|
||||
import { ArrowSmDownIcon } from '@heroicons/react/solid'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const MarketsTable = ({ isPerpMarket }) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.md : false
|
||||
const marketsInfo = useMangoStore((s) => s.marketsInfo)
|
||||
const router = useRouter()
|
||||
|
||||
const perpMarketsInfo = useMemo(
|
||||
() =>
|
||||
|
@ -267,7 +269,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
|||
) : (
|
||||
<div className="mb-4 border-b border-th-bkg-4">
|
||||
<MobileTableHeader
|
||||
colOneHeader={t('asset')}
|
||||
colOneHeader={t('market')}
|
||||
colTwoHeader={`${t('price')}/${t('rolling-change')}`}
|
||||
/>
|
||||
{items.map((market, index) => {
|
||||
|
@ -297,9 +299,9 @@ const MarketsTable = ({ isPerpMarket }) => {
|
|||
className={`mr-2.5`}
|
||||
/>
|
||||
|
||||
{market.baseSymbol}
|
||||
{name}
|
||||
</div>
|
||||
<div className="flex space-x-2.5 text-right text-th-fgd-1">
|
||||
<div className="flex space-x-1.5 text-right text-th-fgd-1">
|
||||
<div>{formatUsdValue(last)}</div>
|
||||
<div className="text-th-fgd-4">|</div>
|
||||
<div
|
||||
|
@ -319,7 +321,7 @@ const MarketsTable = ({ isPerpMarket }) => {
|
|||
key={`${name}${index}`}
|
||||
panelTemplate={
|
||||
<>
|
||||
<div className="grid grid-flow-row grid-cols-2 gap-4 pb-4">
|
||||
<div className="grid grid-flow-row grid-cols-2 gap-4">
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">
|
||||
{t('daily-low')}
|
||||
|
@ -378,6 +380,16 @@ const MarketsTable = ({ isPerpMarket }) => {
|
|||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<Button
|
||||
className="col-span-2"
|
||||
onClick={() =>
|
||||
router.push(`/?name=${name}`, undefined, {
|
||||
shallow: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('trade')}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -103,10 +103,10 @@ const SwitchMarketDropdown = () => {
|
|||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Panel
|
||||
className="thin-scroll absolute left-0 top-14 z-10 max-h-[75vh] w-72 transform overflow-y-auto rounded-b-md rounded-tl-md bg-th-bkg-3 p-4"
|
||||
className="thin-scroll absolute left-0 top-14 z-10 max-h-[50vh] w-72 transform overflow-y-auto rounded-b-md rounded-tl-md bg-th-bkg-3 p-4 sm:max-h-[75vh]"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div className="pb-2.5">
|
||||
<div className="hidden pb-2.5 sm:block">
|
||||
<Input
|
||||
onChange={(e) => onSearch(e.target.value)}
|
||||
prefix={<SearchIcon className="h-4 w-4 text-th-fgd-3" />}
|
||||
|
@ -134,7 +134,7 @@ const SwitchMarketDropdown = () => {
|
|||
<div className="">
|
||||
<div className="flex justify-between py-1.5">
|
||||
<h4 className="text-xs font-normal">{t('futures')}</h4>
|
||||
<p className="mb-0 text-xs text-th-fgd-3">
|
||||
<p className="mb-0 hidden text-xs text-th-fgd-3 sm:block">
|
||||
{t('favorite')}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -148,7 +148,7 @@ const SwitchMarketDropdown = () => {
|
|||
))}
|
||||
<div className="flex justify-between py-1.5">
|
||||
<h4 className="text-xs font-normal">{t('spot')}</h4>
|
||||
<p className="mb-0 text-xs text-th-fgd-3">
|
||||
<p className="mb-0 hidden text-xs text-th-fgd-3 sm:block">
|
||||
{t('favorite')}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -36,12 +36,12 @@ const BottomBar = () => {
|
|||
<div className="default-transition grid grid-cols-5 grid-rows-1 bg-th-bkg-3 py-2.5">
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/select',
|
||||
pathname: '/markets',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
asPath === '/select' ? 'text-th-primary' : 'text-th-fgd-3'
|
||||
asPath === '/markets' ? 'text-th-primary' : 'text-th-fgd-3'
|
||||
} default-transition col-span-1 flex cursor-pointer flex-col items-center hover:text-th-primary`}
|
||||
>
|
||||
<BtcMonoIcon className="mb-1 h-4 w-4" />
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useMemo, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Disclosure } from '@headlessui/react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { SwitchHorizontalIcon, XIcon } from '@heroicons/react/outline'
|
||||
import { XIcon } from '@heroicons/react/outline'
|
||||
import useMangoStore from '../../stores/useMangoStore'
|
||||
import { getWeights, PerpMarket } from '@blockworks-foundation/mango-client'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-client'
|
||||
import { CandlesIcon } from '../icons'
|
||||
import SwipeableTabs from './SwipeableTabs'
|
||||
import AdvancedTradeForm from '../trade_form/AdvancedTradeForm'
|
||||
|
@ -16,8 +16,8 @@ import RecentMarketTrades from '../RecentMarketTrades'
|
|||
import FloatingElement from '../FloatingElement'
|
||||
import Swipeable from './Swipeable'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Link from 'next/link'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import SwitchMarketDropdown from 'components/SwitchMarketDropdown'
|
||||
|
||||
const TVChartContainer = dynamic(
|
||||
() => import('../../components/TradingView/index'),
|
||||
|
@ -30,24 +30,11 @@ const MobileTradePage = () => {
|
|||
const { connected } = useWallet()
|
||||
const selectedMarket = useMangoStore((s) => s.selectedMarket.current)
|
||||
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
|
||||
const baseSymbol = marketConfig.baseSymbol
|
||||
const isPerpMarket = marketConfig.kind === 'perp'
|
||||
|
||||
const handleChangeViewIndex = (index) => {
|
||||
setViewIndex(index)
|
||||
}
|
||||
|
||||
const initLeverage = useMemo(() => {
|
||||
if (!mangoGroup || !marketConfig) return 1
|
||||
|
||||
const ws = getWeights(mangoGroup, marketConfig.marketIndex, 'Init')
|
||||
const w =
|
||||
marketConfig.kind === 'perp' ? ws.perpAssetWeight : ws.spotAssetWeight
|
||||
return Math.round((100 * -1) / (w.toNumber() - 1)) / 100
|
||||
}, [mangoGroup, marketConfig])
|
||||
|
||||
const TABS =
|
||||
selectedMarket instanceof PerpMarket
|
||||
? ['Trade', 'Details', 'Position', 'Orders']
|
||||
|
@ -57,30 +44,7 @@ const MobileTradePage = () => {
|
|||
<div className="px-2 pb-14 pt-4">
|
||||
<div className="relative">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="30"
|
||||
height="30"
|
||||
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
|
||||
className="mr-2"
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<div className="pr-0.5 text-xl font-semibold">{baseSymbol}</div>
|
||||
<span className="text-xl text-th-fgd-4">
|
||||
{isPerpMarket ? '-' : '/'}
|
||||
</span>
|
||||
<div className="pl-0.5 text-xl font-semibold">
|
||||
{isPerpMarket ? 'PERP' : groupConfig.quoteSymbol}
|
||||
</div>
|
||||
</div>
|
||||
<span className="ml-2 rounded border border-th-primary px-1 py-0.5 text-xs text-th-primary">
|
||||
{initLeverage}x
|
||||
</span>
|
||||
<Link href="/select">
|
||||
<div className="ml-2 flex h-10 w-10 items-center justify-center">
|
||||
<SwitchHorizontalIcon className="h-5 w-5" />
|
||||
</div>
|
||||
</Link>
|
||||
<SwitchMarketDropdown />
|
||||
</div>
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
|
|
|
@ -15,11 +15,7 @@ import {
|
|||
InformationCircleIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import { notify } from '../../utils/notifications'
|
||||
import {
|
||||
calculateTradePrice,
|
||||
getDecimalCount,
|
||||
percentFormat,
|
||||
} from '../../utils'
|
||||
import { calculateTradePrice, getDecimalCount } from '../../utils'
|
||||
import { floorToDecimal } from '../../utils/index'
|
||||
import useMangoStore, { Orderbook } from '../../stores/useMangoStore'
|
||||
import Button, { LinkButton } from '../Button'
|
||||
|
@ -79,7 +75,7 @@ export default function AdvancedTradeForm({
|
|||
const [spotMargin, setSpotMargin] = useState(defaultSpotMargin)
|
||||
const [positionSizePercent, setPositionSizePercent] = useState('')
|
||||
const [insufficientSol, setInsufficientSol] = useState(false)
|
||||
const { takerFee, makerFee } = useFees()
|
||||
const { takerFee } = useFees()
|
||||
const { totalMsrm } = useSrmAccount()
|
||||
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
|
@ -947,7 +943,7 @@ export default function AdvancedTradeForm({
|
|||
</div>
|
||||
) : null
|
||||
) : null}
|
||||
<div className="flex-wrap sm:flex">
|
||||
<div className="mt-1 flex-wrap sm:flex">
|
||||
{isLimitOrder ? (
|
||||
<div className="flex">
|
||||
<div className="mr-4 mt-3">
|
||||
|
@ -989,42 +985,44 @@ export default function AdvancedTradeForm({
|
|||
auto updating the reduceOnly state when doing a market order:
|
||||
&& showReduceOnly(perpAccount?.basePosition.toNumber())
|
||||
*/}
|
||||
{marketConfig.kind === 'perp' || isLuna ? (
|
||||
<div className="mr-4 mt-3">
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
delay={250}
|
||||
placement="left"
|
||||
content={t('tooltip-reduce')}
|
||||
>
|
||||
<Checkbox
|
||||
checked={reduceOnly}
|
||||
onChange={(e) => reduceOnChange(e.target.checked)}
|
||||
disabled={isTriggerOrder || isLuna}
|
||||
<div className="flex">
|
||||
{marketConfig.kind === 'perp' || isLuna ? (
|
||||
<div className="mr-4 mt-3">
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
delay={250}
|
||||
placement="left"
|
||||
content={t('tooltip-reduce')}
|
||||
>
|
||||
Reduce Only
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
{marketConfig.kind === 'perp' && tradeType === 'Limit' ? (
|
||||
<div className="mt-3">
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
delay={250}
|
||||
placement="left"
|
||||
content={t('tooltip-post-and-slide')}
|
||||
>
|
||||
<Checkbox
|
||||
checked={postOnlySlide}
|
||||
onChange={(e) => postOnlySlideOnChange(e.target.checked)}
|
||||
disabled={isTriggerOrder}
|
||||
<Checkbox
|
||||
checked={reduceOnly}
|
||||
onChange={(e) => reduceOnChange(e.target.checked)}
|
||||
disabled={isTriggerOrder || isLuna}
|
||||
>
|
||||
Reduce Only
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
{marketConfig.kind === 'perp' && tradeType === 'Limit' ? (
|
||||
<div className="mt-3">
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
delay={250}
|
||||
placement="left"
|
||||
content={t('tooltip-post-and-slide')}
|
||||
>
|
||||
Slide
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
<Checkbox
|
||||
checked={postOnlySlide}
|
||||
onChange={(e) => postOnlySlideOnChange(e.target.checked)}
|
||||
disabled={isTriggerOrder}
|
||||
>
|
||||
Slide
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{marketConfig.kind === 'spot' ? (
|
||||
<div className="mt-3">
|
||||
<Tooltip
|
||||
|
@ -1050,7 +1048,7 @@ export default function AdvancedTradeForm({
|
|||
<div className="text-xs">{t('slippage-warning')}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`mt-3 flex`}>
|
||||
<div className={`mt-4 flex`}>
|
||||
{canTrade ? (
|
||||
<button
|
||||
disabled={disabledTradeButton}
|
||||
|
@ -1174,18 +1172,7 @@ export default function AdvancedTradeForm({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-2.5 flex flex-col items-center justify-center px-6 text-xs text-th-fgd-4 md:flex-row">
|
||||
<div>
|
||||
{t('maker-fee')}: {percentFormat.format(makerFee)}{' '}
|
||||
</div>
|
||||
<span className="hidden md:block md:px-1">|</span>
|
||||
<div>
|
||||
{' '}
|
||||
{t('taker-fee')}: {percentFormat.format(takerFee)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next'
|
|||
import MarketsTable from '../components/MarketsTable'
|
||||
import Tabs from '../components/Tabs'
|
||||
|
||||
const TABS = ['perp', 'spot']
|
||||
const TABS = ['futures', 'spot']
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
|
@ -18,15 +18,13 @@ export async function getStaticProps({ locale }) {
|
|||
}
|
||||
|
||||
export default function Markets() {
|
||||
const [activeTab, setActiveTab] = useState('perp')
|
||||
const [activeTab, setActiveTab] = useState('futures')
|
||||
const { t } = useTranslation(['common'])
|
||||
|
||||
const handleTabChange = (tabName) => {
|
||||
setActiveTab(tabName)
|
||||
}
|
||||
|
||||
const isPerp = activeTab === 'perp'
|
||||
|
||||
return (
|
||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
||||
<TopBar />
|
||||
|
@ -35,13 +33,14 @@ export default function Markets() {
|
|||
<h1>{t('markets')}</h1>
|
||||
</div>
|
||||
<div className="md:rounded-lg md:bg-th-bkg-2 md:p-6">
|
||||
<Tabs activeTab={activeTab} onChange={handleTabChange} tabs={TABS} />
|
||||
<h2 className="mb-4">
|
||||
{isPerp
|
||||
? `${t('perp')} ${t('markets')}`
|
||||
: `${t('spot')} ${t('markets')}`}
|
||||
</h2>
|
||||
<MarketsTable isPerpMarket={activeTab === 'perp'} />
|
||||
<div className="mb-0 sm:mb-6">
|
||||
<Tabs
|
||||
activeTab={activeTab}
|
||||
onChange={handleTabChange}
|
||||
tabs={TABS}
|
||||
/>
|
||||
</div>
|
||||
<MarketsTable isPerpMarket={activeTab === 'futures'} />
|
||||
</div>
|
||||
</PageBodyContainer>
|
||||
</div>
|
||||
|
|
105
pages/select.tsx
105
pages/select.tsx
|
@ -1,105 +0,0 @@
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ChevronRightIcon } from '@heroicons/react/solid'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import Link from 'next/link'
|
||||
import { formatUsdValue } from '../utils'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import PageBodyContainer from '../components/PageBodyContainer'
|
||||
import TopBar from '../components/TopBar'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common', 'tv-chart'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const SelectMarket = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
|
||||
|
||||
const [markets, setMarkets] = useState<any[]>([])
|
||||
useEffect(() => {
|
||||
const markets: any[] = []
|
||||
const allMarkets =
|
||||
groupConfig?.spotMarkets && groupConfig?.perpMarkets
|
||||
? [...groupConfig.spotMarkets, ...groupConfig.perpMarkets]
|
||||
: []
|
||||
allMarkets.forEach((market) => {
|
||||
const base = market.name.slice(0, -5)
|
||||
const found = markets.find((b) => b.baseAsset === base)
|
||||
if (!found) {
|
||||
markets.push({ baseAsset: base, markets: [market] })
|
||||
} else {
|
||||
found.markets.push(market)
|
||||
}
|
||||
})
|
||||
setMarkets(markets)
|
||||
}, [])
|
||||
|
||||
if (!mangoCache) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
||||
<TopBar />
|
||||
<PageBodyContainer>
|
||||
<div className="py-4 text-2xl font-bold text-th-fgd-1">
|
||||
{t('markets')}
|
||||
</div>
|
||||
{markets.map((mkt) => {
|
||||
return (
|
||||
<div key={mkt.baseAsset}>
|
||||
<div className="flex items-center justify-between bg-th-bkg-3 px-2.5 py-2">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
src={`/assets/icons/${mkt.baseAsset.toLowerCase()}.svg`}
|
||||
className={`mr-2.5 h-5 w-auto`}
|
||||
/>
|
||||
<span className="text-th-fgd-2">{mkt.baseAsset}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y divide-th-bkg-4">
|
||||
{mangoGroup
|
||||
? mkt.markets.map((m) => (
|
||||
<div
|
||||
className={`flex items-center justify-between px-2.5 text-xs`}
|
||||
key={m.name}
|
||||
>
|
||||
<Link href={`/?name=${m.name}`} key={m.name}>
|
||||
<a className="default-transition flex h-12 w-full cursor-pointer items-center justify-between text-th-fgd-2 hover:text-th-primary">
|
||||
{m.name}
|
||||
<div className="flex items-center">
|
||||
<span className="w-20 text-right">
|
||||
{formatUsdValue(
|
||||
mangoGroup
|
||||
.getPrice(m.marketIndex, mangoCache)
|
||||
.toNumber()
|
||||
)}
|
||||
</span>
|
||||
<ChevronRightIcon className="ml-1 h-4 w-5 text-th-fgd-2" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{/* spacer so last market can be selected albeit bottom bar overlay */}
|
||||
<p className="flex h-12 md:hidden"></p>
|
||||
</PageBodyContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SelectMarket
|
Loading…
Reference in New Issue