prevent extra rerenders
This commit is contained in:
parent
0c3ea08764
commit
faa466ca17
|
@ -61,9 +61,7 @@ const FeeDiscountsTable = () => {
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button disabled>Connect Wallet</Button>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
{showDeposit && (
|
||||
<DepositSrmModal isOpen={showDeposit} onClose={handleCloseDeposit} />
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import React from 'react'
|
||||
import React, { FunctionComponent } from 'react'
|
||||
|
||||
export default function FloatingElement({ shrink = false, children }) {
|
||||
type FloatingElementProps = {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const FloatingElement: FunctionComponent<FloatingElementProps> = ({
|
||||
className,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`m-1 p-4 bg-th-bkg-2 rounded-lg overflow-auto ${
|
||||
shrink ? null : `h-full`
|
||||
}`}
|
||||
className={`p-2 h-full bg-th-bkg-2 rounded-lg overflow-hidden ${className}`}
|
||||
>
|
||||
{children}
|
||||
<div className="h-full overflow-auto thin-scroll p-2">{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloatingElement
|
||||
|
|
|
@ -13,7 +13,7 @@ import DepositModal from './DepositModal'
|
|||
import WithdrawModal from './WithdrawModal'
|
||||
import Button from './Button'
|
||||
|
||||
export default function MarginStats() {
|
||||
export default function MarginBalances() {
|
||||
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const selectedMarginAccount = useMangoStore(
|
||||
(s) => s.selectedMarginAccount.current
|
||||
|
|
|
@ -3,9 +3,8 @@ import { useEffect, useState } from 'react'
|
|||
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
|
||||
import { groupBy } from '../utils'
|
||||
import useTradeHistory from '../hooks/useTradeHistory'
|
||||
import useConnection from '../hooks/useConnection'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import FloatingElement from './FloatingElement'
|
||||
import useMarginAccount from '../hooks/useMarginAccount'
|
||||
|
||||
const calculatePNL = (tradeHistory, prices, mangoGroup) => {
|
||||
if (!tradeHistory.length) return '0.00'
|
||||
|
@ -52,9 +51,12 @@ const calculatePNL = (tradeHistory, prices, mangoGroup) => {
|
|||
return total.toFixed(2)
|
||||
}
|
||||
|
||||
export default function MarginStats() {
|
||||
const { connection } = useConnection()
|
||||
const { marginAccount, mangoGroup } = useMarginAccount()
|
||||
export default function MarginInfo() {
|
||||
const connection = useMangoStore((s) => s.connection.current)
|
||||
const selectedMarginAccount = useMangoStore(
|
||||
(s) => s.selectedMarginAccount.current
|
||||
)
|
||||
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const [mAccountInfo, setMAccountInfo] = useState<
|
||||
| {
|
||||
label: string
|
||||
|
@ -65,24 +67,28 @@ export default function MarginStats() {
|
|||
}[]
|
||||
| null
|
||||
>(null)
|
||||
const { tradeHistory } = useTradeHistory()
|
||||
const tradeHistory = useTradeHistory()
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoGroup) {
|
||||
mangoGroup.getPrices(connection).then((prices) => {
|
||||
const collateralRatio = marginAccount
|
||||
? marginAccount.getCollateralRatio(mangoGroup, prices)
|
||||
if (selectedMangoGroup) {
|
||||
selectedMangoGroup.getPrices(connection).then((prices) => {
|
||||
const collateralRatio = selectedMarginAccount
|
||||
? selectedMarginAccount.getCollateralRatio(selectedMangoGroup, prices)
|
||||
: 200
|
||||
|
||||
const accountEquity = marginAccount
|
||||
? marginAccount.computeValue(mangoGroup, prices)
|
||||
const accountEquity = selectedMarginAccount
|
||||
? selectedMarginAccount.computeValue(selectedMangoGroup, prices)
|
||||
: 0
|
||||
let leverage
|
||||
if (marginAccount) {
|
||||
if (selectedMarginAccount) {
|
||||
leverage = accountEquity
|
||||
? (
|
||||
1 /
|
||||
(marginAccount.getCollateralRatio(mangoGroup, prices) - 1)
|
||||
(selectedMarginAccount.getCollateralRatio(
|
||||
selectedMangoGroup,
|
||||
prices
|
||||
) -
|
||||
1)
|
||||
).toFixed(2)
|
||||
: '∞'
|
||||
} else {
|
||||
|
@ -106,7 +112,7 @@ export default function MarginStats() {
|
|||
},
|
||||
{
|
||||
label: 'Total PNL',
|
||||
value: calculatePNL(tradeHistory, prices, mangoGroup),
|
||||
value: calculatePNL(tradeHistory, prices, selectedMangoGroup),
|
||||
unit: '',
|
||||
currency: '$',
|
||||
desc:
|
||||
|
@ -123,7 +129,7 @@ export default function MarginStats() {
|
|||
},
|
||||
{
|
||||
label: 'Maint. Collateral Ratio',
|
||||
value: (mangoGroup.maintCollRatio * 100).toFixed(0),
|
||||
value: (selectedMangoGroup.maintCollRatio * 100).toFixed(0),
|
||||
unit: '%',
|
||||
currency: '',
|
||||
desc:
|
||||
|
@ -131,7 +137,7 @@ export default function MarginStats() {
|
|||
},
|
||||
{
|
||||
label: 'Initial Collateral Ratio',
|
||||
value: (mangoGroup.initCollRatio * 100).toFixed(0),
|
||||
value: (selectedMangoGroup.initCollRatio * 100).toFixed(0),
|
||||
currency: '',
|
||||
unit: '%',
|
||||
desc: 'The collateral ratio required to open a new margin position',
|
||||
|
@ -140,7 +146,7 @@ export default function MarginStats() {
|
|||
})
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [marginAccount, mangoGroup])
|
||||
}, [selectedMarginAccount, selectedMangoGroup])
|
||||
return (
|
||||
<FloatingElement>
|
||||
<>
|
|
@ -13,7 +13,6 @@ import MenuItem from './MenuItem'
|
|||
import ThemeSwitch from './ThemeSwitch'
|
||||
import WalletIcon from './WalletIcon'
|
||||
import UiLock from './UiLock'
|
||||
import DropMenu from './DropMenu'
|
||||
import { useRouter } from 'next/router'
|
||||
import WalletSelect from './WalletSelect'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import useTradeHistory from '../hooks/useTradeHistory'
|
||||
|
||||
const TradeHistoryTable = () => {
|
||||
const { tradeHistory } = useTradeHistory()
|
||||
const tradeHistory = useTradeHistory()
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col py-6`}>
|
||||
|
@ -61,7 +61,7 @@ const TradeHistoryTable = () => {
|
|||
<tbody>
|
||||
{tradeHistory.map((trade, index) => (
|
||||
<tr
|
||||
key={`${trade.orderId}${trade.side}`}
|
||||
key={`${trade.orderId}${trade.side}${trade.uuid}`}
|
||||
className={`
|
||||
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
|
||||
`}
|
||||
|
|
|
@ -7,7 +7,7 @@ const TVChartContainer = dynamic(
|
|||
)
|
||||
import FloatingElement from '../components/FloatingElement'
|
||||
import Orderbook from '../components/Orderbook'
|
||||
import MarginStats from './MarginStats'
|
||||
import MarginInfo from './MarginInfo'
|
||||
import MarginBalances from './MarginBalances'
|
||||
import TradeForm from './TradeForm'
|
||||
import UserInfo from './UserInfo'
|
||||
|
@ -21,7 +21,7 @@ const layouts = {
|
|||
{ i: 'tvChart', x: 0, y: 0, w: 3, h: 30 },
|
||||
{ i: 'orderbook', x: 3, y: 0, w: 1, h: 17 },
|
||||
{ i: 'tradeForm', x: 4, y: 0, w: 1, h: 17 },
|
||||
{ i: 'marginStats', x: 4, y: 2, w: 1, h: 12 },
|
||||
{ i: 'marginInfo', x: 4, y: 2, w: 1, h: 12 },
|
||||
{ i: 'marketTrades', x: 3, y: 1, w: 1, h: 13 },
|
||||
{ i: 'userInfo', x: 0, y: 2, w: 4, h: 17 },
|
||||
{ i: 'balanceInfo', x: 4, y: 1, w: 1, h: 13 },
|
||||
|
@ -29,7 +29,7 @@ const layouts = {
|
|||
lg: [
|
||||
{ i: 'tvChart', x: 0, y: 0, w: 2, h: 24 },
|
||||
{ i: 'balanceInfo', x: 2, y: 0, w: 1, h: 13 },
|
||||
{ i: 'marginStats', x: 2, y: 1, w: 1, h: 11 },
|
||||
{ i: 'marginInfo', x: 2, y: 1, w: 1, h: 11 },
|
||||
{ i: 'orderbook', x: 0, y: 2, w: 1, h: 17 },
|
||||
{ i: 'tradeForm', x: 1, y: 2, w: 1, h: 17 },
|
||||
{ i: 'marketTrades', x: 2, y: 2, w: 1, h: 17 },
|
||||
|
@ -63,8 +63,8 @@ const TradePageGrid = () => {
|
|||
<div key="tradeForm">
|
||||
<TradeForm />
|
||||
</div>
|
||||
<div key="marginStats">
|
||||
<MarginStats />
|
||||
<div key="marginInfo">
|
||||
<MarginInfo />
|
||||
</div>
|
||||
<div key="userInfo">
|
||||
<UserInfo />
|
||||
|
|
|
@ -15,12 +15,12 @@ const UiLock = ({ className = '' }) => {
|
|||
<div className={`flex relative ${className}`}>
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="w-10 h-10 flex items-center justify-center hover:text-th-primary rounded-mdbg-transparent rounded hover:text-th-primary focus:outline-none"
|
||||
className="w-10 h-10 flex items-center justify-center bg-transparent rounded hover:text-th-primary focus:outline-none"
|
||||
>
|
||||
{uiLocked ? (
|
||||
<LockClosedIcon className="w-5 h-5" />
|
||||
) : (
|
||||
<LockOpenIcon className="w-5 h-5" />
|
||||
<LockOpenIcon className="w-5 h-5 animate-bounce" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -4,9 +4,7 @@ import { IDS } from '@blockworks-foundation/mango-client'
|
|||
import useMangoStore from '../stores/useMangoStore'
|
||||
|
||||
const useConnection = () => {
|
||||
// console.log('loading useConnection')
|
||||
|
||||
const setSolanaStore = useMangoStore((s) => s.set)
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const { cluster, current: connection, endpoint } = useMangoStore(
|
||||
(s) => s.connection
|
||||
)
|
||||
|
@ -16,17 +14,17 @@ const useConnection = () => {
|
|||
])
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
if (connection && endpoint === connection._rpcEndpoint) return
|
||||
if (connection && endpoint === connection['_rpcEndpoint']) return
|
||||
console.log('setting new connection')
|
||||
|
||||
const newConnection = new Connection(endpoint, 'recent')
|
||||
setSolanaStore((state) => {
|
||||
setMangoStore((state) => {
|
||||
state.connection.current = newConnection
|
||||
})
|
||||
}, [endpoint])
|
||||
|
||||
useEffect(() => {
|
||||
if (connection && endpoint === connection['_rpcEndpoint']) return
|
||||
const id = connection.onAccountChange(new Account().publicKey, () => {})
|
||||
return () => {
|
||||
connection.removeAccountChangeListener(id)
|
||||
|
@ -34,6 +32,7 @@ const useConnection = () => {
|
|||
}, [endpoint])
|
||||
|
||||
useEffect(() => {
|
||||
if (connection && endpoint === connection['_rpcEndpoint']) return
|
||||
const id = connection.onSlotChange(() => null)
|
||||
return () => {
|
||||
connection.removeSlotChangeListener(id)
|
||||
|
|
|
@ -18,9 +18,14 @@ const useHydrateStore = () => {
|
|||
const setSerumStore = useSerumStore((s) => s.set)
|
||||
const selectedMarketAddress = useMangoStore(marketAddressSelector)
|
||||
const marketsForSelectedMangoGroup = useMangoStore(mangoGroupMarketsSelector)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const { connection, dexProgramId } = useConnection()
|
||||
const { marketList } = useMarketList()
|
||||
|
||||
useEffect(() => {
|
||||
actions.fetchMangoGroup()
|
||||
}, [actions])
|
||||
|
||||
// load selected market
|
||||
useEffect(() => {
|
||||
Market.load(
|
||||
|
@ -76,7 +81,7 @@ const useHydrateStore = () => {
|
|||
})
|
||||
}, [marketList])
|
||||
|
||||
// hydrate orderbook for all markets in mango group
|
||||
// hydrate orderbook with all markets in mango group
|
||||
useEffect(() => {
|
||||
const subscriptionIds = Object.entries(marketsForSelectedMangoGroup).map(
|
||||
([, market]) => {
|
||||
|
@ -131,6 +136,7 @@ const useHydrateStore = () => {
|
|||
}
|
||||
}, [marketsForSelectedMangoGroup])
|
||||
|
||||
// fetch filled trades for selected market
|
||||
useInterval(() => {
|
||||
async function fetchFills() {
|
||||
const market = useMangoStore.getState().market.current
|
||||
|
|
|
@ -23,12 +23,6 @@ const useMarginAccount = () => {
|
|||
}
|
||||
}, [connected, actions])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedMarginAccount) {
|
||||
actions.fetchTradeHistory()
|
||||
}
|
||||
}, [selectedMarginAccount])
|
||||
|
||||
useInterval(() => {
|
||||
if (connected) {
|
||||
actions.fetchMarginAccounts()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import useSerumStore from '../stores/useSerumStore'
|
||||
import useMarket from './useMarket'
|
||||
|
@ -19,7 +19,7 @@ const formatTradeHistory = (newTradeHistory) => {
|
|||
marketName: trade.marketName
|
||||
? trade.marketName
|
||||
: `${trade.baseCurrency}/${trade.quoteCurrency}`,
|
||||
key: `${trade.orderId}${trade.side}${trade.uuid}`,
|
||||
key: `${trade.orderId}-${trade.uuid}`,
|
||||
liquidity: trade.maker || trade?.eventFlags?.maker ? 'Maker' : 'Taker',
|
||||
}
|
||||
})
|
||||
|
@ -27,7 +27,17 @@ const formatTradeHistory = (newTradeHistory) => {
|
|||
}
|
||||
|
||||
const useFills = () => {
|
||||
const fills = useSerumStore((s) => s.fills)
|
||||
const fillsRef = useRef(useSerumStore.getState().fills)
|
||||
const fills = fillsRef.current
|
||||
useEffect(
|
||||
() =>
|
||||
useSerumStore.subscribe(
|
||||
(fills) => (fillsRef.current = fills as []),
|
||||
(state) => state.fills
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
const { market, marketName } = useMarket()
|
||||
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
|
||||
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
|
@ -44,7 +54,6 @@ const useFills = () => {
|
|||
|
||||
export const useTradeHistory = () => {
|
||||
const eventQueueFills = useFills()
|
||||
const [allTrades, setAllTrades] = useState<any[]>([])
|
||||
const tradeHistory = useMangoStore((s) => s.tradeHistory)
|
||||
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
|
@ -55,22 +64,19 @@ export const useTradeHistory = () => {
|
|||
}
|
||||
}, 12000)
|
||||
|
||||
useEffect(() => {
|
||||
if (eventQueueFills && eventQueueFills.length > 0) {
|
||||
const newFills = eventQueueFills.filter(
|
||||
(fill) =>
|
||||
!tradeHistory.find((t) => t.orderId === fill.orderId.toString())
|
||||
)
|
||||
const newTradeHistory = [...newFills, ...tradeHistory]
|
||||
if (newFills.length > 0 && newTradeHistory.length !== allTrades.length) {
|
||||
const formattedTradeHistory = formatTradeHistory(newTradeHistory)
|
||||
|
||||
setAllTrades(formattedTradeHistory)
|
||||
}
|
||||
const allTrades = []
|
||||
if (eventQueueFills && eventQueueFills.length > 0) {
|
||||
const newFills = eventQueueFills.filter(
|
||||
(fill) =>
|
||||
!tradeHistory.flat().find((t) => t.orderId === fill.orderId.toString())
|
||||
)
|
||||
const newTradeHistory = [...newFills, ...tradeHistory]
|
||||
if (newFills.length > 0 && newTradeHistory.length !== allTrades.length) {
|
||||
return formatTradeHistory(newTradeHistory)
|
||||
}
|
||||
}, [tradeHistory, eventQueueFills])
|
||||
}
|
||||
|
||||
return { tradeHistory: allTrades }
|
||||
return tradeHistory.flat()
|
||||
}
|
||||
|
||||
export default useTradeHistory
|
||||
|
|
|
@ -191,7 +191,7 @@ export default function StatsPage() {
|
|||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all `}>
|
||||
<TopBar />
|
||||
<div className="min-h-screen w-full lg:w-2/3 mx-auto p-1 sm:px-2 sm:py-1 md:px-6 md:py-1">
|
||||
<FloatingElement>
|
||||
<FloatingElement className="h-auto">
|
||||
<div className="text-center">
|
||||
<h1 className={`text-th-fgd-1 text-3xl`}>Mango Stats</h1>
|
||||
</div>
|
||||
|
@ -253,7 +253,7 @@ export default function StatsPage() {
|
|||
</div>
|
||||
</FloatingElement>
|
||||
{selectedAsset ? (
|
||||
<FloatingElement shrink>
|
||||
<FloatingElement className="h-auto">
|
||||
<div className="flex justify-center text-2xl">
|
||||
<span className={`text-th-fgd-1`}>Historical</span>
|
||||
<Select
|
||||
|
|
|
@ -67,78 +67,37 @@ button {
|
|||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
/* display: none; <- Crashes Chrome on hover */
|
||||
-webkit-appearance: none;
|
||||
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield; /* Firefox */
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
/* TODO: remove. styling for old ant components below */
|
||||
|
||||
.ant-input-group-addon {
|
||||
background-color: transparent !important;
|
||||
.thin-scroll {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.ant-switch {
|
||||
background-color: #5b5868 !important;
|
||||
.thin-scroll:hover {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.ant-switch-checked {
|
||||
background-color: #f2c94c !important;
|
||||
.thin-scroll::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
background-color: var(--bkg-2);
|
||||
}
|
||||
|
||||
.ant-switch-handle::before {
|
||||
background-color: #ffffff !important;
|
||||
.thin-scroll::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background-color: var(--bkg-3);
|
||||
}
|
||||
|
||||
.ant-input-affix-wrapper-disabled {
|
||||
background-color: #262337 !important;
|
||||
color: #ffffff !important;
|
||||
.thin-scroll::-webkit-scrollbar-track {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.ant-select-arrow {
|
||||
color: #817f8a !important;
|
||||
}
|
||||
|
||||
.ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
|
||||
color: #f2c94c !important;
|
||||
font-weight: 600 !important;
|
||||
background-color: #141026 !important;
|
||||
}
|
||||
|
||||
.ant-select-item-option-active:not(.ant-select-item-option-disabled) {
|
||||
background-color: #584f81 !important;
|
||||
}
|
||||
|
||||
.ant-input-group::after {
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
.ant-input-group::before {
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
.ant-input-group-wrapper {
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
.ant-input-group:first-child .ant-input-affix-wrapper:not(:first-child) {
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
.ant-input-group.ant-input-group-compact > *:last-child,
|
||||
.ant-input-group.ant-input-group-compact
|
||||
> .ant-select:last-child
|
||||
> .ant-select-selector,
|
||||
.ant-input-group.ant-input-group-compact
|
||||
> .ant-cascader-picker:last-child
|
||||
.ant-input,
|
||||
.ant-input-group.ant-input-group-compact
|
||||
> .ant-cascader-picker-focused:last-child
|
||||
.ant-input {
|
||||
border-color: #524a79 !important;
|
||||
.thin-scroll::-webkit-scrollbar-corner {
|
||||
background-color: var(--bkg-3);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ module.exports = {
|
|||
fontFamily: {
|
||||
sans: ['Nunito'],
|
||||
},
|
||||
fontSize: {
|
||||
xxs: '.6rem',
|
||||
},
|
||||
colors: {
|
||||
'mango-orange': {
|
||||
DEFAULT: '#DFAB01',
|
||||
|
@ -94,20 +97,6 @@ module.exports = {
|
|||
'th-green': 'var(--green)',
|
||||
},
|
||||
},
|
||||
fontSize: {
|
||||
xxs: '.6rem',
|
||||
// 'sm': '.875rem',
|
||||
// 'tiny': '.875rem',
|
||||
// 'base': '1rem',
|
||||
// 'lg': '1.125rem',
|
||||
// 'xl': '1.25rem',
|
||||
// '2xl': '1.5rem',
|
||||
// '3xl': '1.875rem',
|
||||
// '4xl': '2.25rem',
|
||||
// '5xl': '3rem',
|
||||
// '6xl': '4rem',
|
||||
// '7xl': '5rem',
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
extend: {
|
||||
|
|
Loading…
Reference in New Issue