diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index c4cff553a..d7f5ee5a5 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -159,10 +159,26 @@ export const TradeMarketHeader = ({ > - - - - + + + + {asset ? ( { - const [candlesClose, setCandlesClose] = useState([]); +interface Props { + marketId?: string; + decimalPlaces?: number; + initialValue?: string[]; + isHeader?: boolean; + noUpdate?: boolean; +} + +export const Last24hPriceChange = ({ + marketId, + decimalPlaces, + initialValue, + isHeader = false, + noUpdate = false, +}: Props) => { + const [candlesClose, setCandlesClose] = useState( + initialValue || [] + ); const yesterday = useYesterday(); // Cache timestamp for yesterday to prevent full unmount of market page when // a rerender occurs @@ -24,13 +39,6 @@ export const Last24hPriceChange = ({ marketId }: { marketId?: string }) => { return new Date(yesterday).toISOString(); }, [yesterday]); - const marketVariables = useMemo( - () => ({ - marketId: marketId, - }), - [marketId] - ); - const variables = useMemo( () => ({ marketId: marketId, @@ -40,18 +48,14 @@ export const Last24hPriceChange = ({ marketId }: { marketId?: string }) => { [marketId, yTimestamp] ); - const { data, error } = useDataProvider({ - dataProvider: marketProvider, - variables: marketVariables, - skip: !marketId, - }); - const throttledSetCandles = useRef( throttle((data: Candle[]) => { - const candlesClose: string[] = data - .map((candle) => candle?.close) - .filter((c): c is CandleClose => c !== null); - setCandlesClose(candlesClose); + if (!noUpdate) { + const candlesClose: string[] = data + .map((candle) => candle?.close) + .filter((c): c is CandleClose => c !== null); + setCandlesClose(candlesClose); + } }, constants.DEBOUNCE_UPDATE_TIME) ).current; const update = useCallback( @@ -64,23 +68,27 @@ export const Last24hPriceChange = ({ marketId }: { marketId?: string }) => { [throttledSetCandles] ); - useDataProvider({ + const { error } = useDataProvider({ dataProvider: marketCandlesProvider, update, variables, - skip: !marketId || !data, + skip: noUpdate || !marketId, }); - return ( + const content = useMemo(() => { + if (error || !isNumeric(decimalPlaces)) { + return <>-; + } + return ( + + ); + }, [candlesClose, decimalPlaces, error]); + + return isHeader ? ( - {!error && data?.decimalPlaces ? ( - - ) : ( - '-' - )} + {content} + ) : ( + content ); }; diff --git a/apps/trading/components/last-24h-volume/last-24h-volume.tsx b/apps/trading/components/last-24h-volume/last-24h-volume.tsx index 399f4545c..3236be933 100644 --- a/apps/trading/components/last-24h-volume/last-24h-volume.tsx +++ b/apps/trading/components/last-24h-volume/last-24h-volume.tsx @@ -1,27 +1,37 @@ import { calcCandleVolume, marketCandlesProvider, - marketProvider, } from '@vegaprotocol/market-list'; import { addDecimalsFormatNumber, t, useDataProvider, useYesterday, + isNumeric, } from '@vegaprotocol/react-helpers'; import { Schema } from '@vegaprotocol/types'; import throttle from 'lodash/throttle'; import { useCallback, useMemo, useRef, useState } from 'react'; - import * as constants from '../constants'; import { HeaderStat } from '../header'; +import type { Candle } from '@vegaprotocol/market-list'; -import type { - SingleMarketFieldsFragment, - Candle, -} from '@vegaprotocol/market-list'; -export const Last24hVolume = ({ marketId }: { marketId?: string }) => { - const [candleVolume, setCandleVolume] = useState(); +interface Props { + marketId?: string; + positionDecimalPlaces?: number; + noUpdate?: boolean; + isHeader?: boolean; + initialValue?: string; +} + +export const Last24hVolume = ({ + marketId, + positionDecimalPlaces, + noUpdate = false, + isHeader = false, + initialValue, +}: Props) => { + const [candleVolume, setCandleVolume] = useState(initialValue || ''); const yesterday = useYesterday(); // Cache timestamp for yesterday to prevent full unmount of market page when // a rerender occurs @@ -29,13 +39,6 @@ export const Last24hVolume = ({ marketId }: { marketId?: string }) => { return new Date(yesterday).toISOString(); }, [yesterday]); - const marketVariables = useMemo( - () => ({ - marketId: marketId, - }), - [marketId] - ); - const variables = useMemo( () => ({ marketId: marketId, @@ -45,15 +48,9 @@ export const Last24hVolume = ({ marketId }: { marketId?: string }) => { [marketId, yTimestamp] ); - const { data, error } = useDataProvider({ - dataProvider: marketProvider, - variables: marketVariables, - skip: !marketId, - }); - const throttledSetCandles = useRef( throttle((data: Candle[]) => { - setCandleVolume(calcCandleVolume(data)); + noUpdate || setCandleVolume(calcCandleVolume(data) || ''); }, constants.DEBOUNCE_UPDATE_TIME) ).current; const update = useCallback( @@ -66,26 +63,40 @@ export const Last24hVolume = ({ marketId }: { marketId?: string }) => { [throttledSetCandles] ); - useDataProvider({ + const { error } = useDataProvider({ dataProvider: marketCandlesProvider, update, variables, - skip: !marketId || !data, + skip: noUpdate || !marketId, }); - return ( + const formatDecimals = isHeader ? positionDecimalPlaces || 0 : 2; + const content = useMemo(() => { + return ( + <> + {!error && candleVolume && isNumeric(positionDecimalPlaces) + ? addDecimalsFormatNumber( + candleVolume, + positionDecimalPlaces, + formatDecimals + ) + : '-'} + + ); + }, [error, candleVolume, positionDecimalPlaces, formatDecimals]); + return isHeader ? ( - {!error && candleVolume && data?.positionDecimalPlaces - ? addDecimalsFormatNumber(candleVolume, data.positionDecimalPlaces) - : '-'} + {content} + ) : ( + content ); }; diff --git a/apps/trading/components/market-mark-price/market-mark-price.tsx b/apps/trading/components/market-mark-price/market-mark-price.tsx index 6f64bf25c..3f45eebc1 100644 --- a/apps/trading/components/market-mark-price/market-mark-price.tsx +++ b/apps/trading/components/market-mark-price/market-mark-price.tsx @@ -3,57 +3,82 @@ import throttle from 'lodash/throttle'; import { addDecimalsFormatNumber, t, + PriceCell, useDataProvider, + isNumeric, } from '@vegaprotocol/react-helpers'; import type { MarketData, MarketDataUpdateFieldsFragment, - SingleMarketFieldsFragment, } from '@vegaprotocol/market-list'; -import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list'; +import { marketDataProvider } from '@vegaprotocol/market-list'; import { HeaderStat } from '../header'; import * as constants from '../constants'; -export const MarketMarkPrice = ({ marketId }: { marketId?: string }) => { - const [marketPrice, setMarketPrice] = useState(null); +interface Props { + marketId?: string; + decimalPlaces?: number; + isHeader?: boolean; + noUpdate?: boolean; + initialValue?: string; +} + +export const MarketMarkPrice = ({ + marketId, + decimalPlaces, + initialValue, + isHeader = false, + noUpdate = false, +}: Props) => { + const [marketPrice, setMarketPrice] = useState( + initialValue || null + ); const variables = useMemo( () => ({ marketId: marketId, }), [marketId] ); - const { data } = useDataProvider({ - dataProvider: marketProvider, - variables, - skip: !marketId, - }); + const throttledSetMarketPrice = useRef( throttle((price: string) => { - setMarketPrice(price); + noUpdate || setMarketPrice(price); }, constants.DEBOUNCE_UPDATE_TIME) ).current; const update = useCallback( ({ data: marketData }: { data: MarketData | null }) => { - throttledSetMarketPrice( - marketData?.markPrice && data?.decimalPlaces - ? addDecimalsFormatNumber(marketData.markPrice, data.decimalPlaces) - : '-' - ); + throttledSetMarketPrice(marketData?.markPrice || ''); return true; }, - [data?.decimalPlaces, throttledSetMarketPrice] + [throttledSetMarketPrice] ); useDataProvider({ dataProvider: marketDataProvider, update, variables, - skip: !marketId || !data, + skip: noUpdate || !marketId, }); - return ( + const content = useMemo(() => { + if (!marketPrice || !isNumeric(decimalPlaces)) { + return <>-; + } + return isHeader ? ( +
{addDecimalsFormatNumber(marketPrice, decimalPlaces)}
+ ) : ( + + ); + }, [marketPrice, decimalPlaces, isHeader]); + + return isHeader ? ( -
{marketPrice}
+ {content}
+ ) : ( + content ); }; diff --git a/apps/trading/components/market-trading-mode/market-trading-mode.tsx b/apps/trading/components/market-trading-mode/market-trading-mode.tsx index 30b3e01ab..54d6c9478 100644 --- a/apps/trading/components/market-trading-mode/market-trading-mode.tsx +++ b/apps/trading/components/market-trading-mode/market-trading-mode.tsx @@ -15,16 +15,30 @@ import type { } from '@vegaprotocol/market-list'; import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list'; import { HeaderStat } from '../header'; +import { Tooltip } from '@vegaprotocol/ui-toolkit'; interface Props { marketId?: string; onSelect?: (marketId: string) => void; + isHeader?: boolean; + noUpdate?: boolean; + initialMode?: Types.MarketTradingMode; + initialTrigger?: Types.AuctionTrigger; } -export const MarketTradingModeComponent = ({ marketId, onSelect }: Props) => { +export const MarketTradingModeComponent = ({ + marketId, + onSelect, + isHeader = false, + noUpdate = false, + initialMode, + initialTrigger, +}: Props) => { const [tradingMode, setTradingMode] = - useState(null); - const [trigger, setTrigger] = useState(null); + useState(initialMode || null); + const [trigger, setTrigger] = useState( + initialTrigger || null + ); const [market, setMarket] = useState(null); const variables = useMemo( () => ({ @@ -41,7 +55,7 @@ export const MarketTradingModeComponent = ({ marketId, onSelect }: Props) => { const update = useCallback( ({ data: marketData }: { data: MarketData | null }) => { - if (marketData) { + if (!noUpdate && marketData) { setTradingMode(marketData.marketTradingMode); setTrigger(marketData.trigger); setMarket({ @@ -51,24 +65,25 @@ export const MarketTradingModeComponent = ({ marketId, onSelect }: Props) => { } return true; }, - [data] + [noUpdate, data] ); useDataProvider({ dataProvider: marketDataProvider, update, variables, - skip: !marketId || !data, + skip: noUpdate || !marketId || !data, }); const content = - tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && + (tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && trigger && trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED ? `${MarketTradingModeMapping[tradingMode]} - ${AuctionTriggerMapping[trigger]}` - : MarketTradingModeMapping[tradingMode as Types.MarketTradingMode]; + : MarketTradingModeMapping[tradingMode as Types.MarketTradingMode]) || + '-'; - return ( + return isHeader ? ( { } testId="market-trading-mode" > -
{content || '-'}
+
{content}
+ ) : ( + + ) + } + > + {content} + ); }; diff --git a/apps/trading/components/select-market/select-market-columns.tsx b/apps/trading/components/select-market/select-market-columns.tsx index e8a2f45ef..1d6449541 100644 --- a/apps/trading/components/select-market/select-market-columns.tsx +++ b/apps/trading/components/select-market/select-market-columns.tsx @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { TradingModeTooltip } from '@vegaprotocol/deal-ticket'; import { FeesCell } from '@vegaprotocol/market-info'; import { calcCandleHigh, @@ -12,56 +10,23 @@ import { signedNumberCssClass, t, } from '@vegaprotocol/react-helpers'; -import { - AuctionTriggerMapping, - MarketTradingModeMapping, - Schema, -} from '@vegaprotocol/types'; -import { - Link as UILink, - PriceCellChange, - Sparkline, - Tooltip, -} from '@vegaprotocol/ui-toolkit'; +import { Link as UILink, Sparkline, Tooltip } from '@vegaprotocol/ui-toolkit'; import isNil from 'lodash/isNil'; - import type { CandleClose } from '@vegaprotocol/types'; import type { MarketWithData, MarketWithCandles, } from '@vegaprotocol/market-list'; import { Link } from 'react-router-dom'; +import { MarketMarkPrice } from '../market-mark-price'; +import { Last24hPriceChange } from '../last-24h-price-change'; +import { MarketTradingModeComponent } from '../market-trading-mode'; +import { Last24hVolume } from '../last-24h-volume'; type Market = MarketWithData & MarketWithCandles; export const cellClassNames = 'py-1 first:text-left text-right'; -const TradingMode = ({ market }: { market: Market }) => { - return ( - - ) - } - > - - {market.tradingMode === - Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && - market.data?.trigger && - market.data.trigger !== - Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED - ? `${MarketTradingModeMapping[market.tradingMode]} - - ${AuctionTriggerMapping[market.data.trigger]}` - : MarketTradingModeMapping[market.tradingMode]} - - - ); -}; - const FeesInfo = () => { return ( void, - onCellClick: OnCellClickHandler + onCellClick: OnCellClickHandler, + activeMarketId?: string | null ) => { const candlesClose = market.candles ?.map((candle) => candle?.close) @@ -221,6 +187,7 @@ export const columns = ( return onSelect(id); } }; + const noUpdate = !activeMarketId || market.id !== activeMarketId; const selectMarketColumns: Column[] = [ { kind: ColumnKind.Market, @@ -248,27 +215,25 @@ export const columns = ( }, { kind: ColumnKind.LastPrice, - value: market.data?.markPrice ? ( - - ) : ( - '-' ), className: cellClassNames, onlyOnDetailed: false, }, { kind: ColumnKind.Change24, - value: candlesClose && ( - ), className: cellClassNames, @@ -345,20 +310,28 @@ export const columns = ( }, { kind: ColumnKind.Volume, - value: candleVolume - ? addDecimalsFormatNumber( - candleVolume.toString(), - market.positionDecimalPlaces, - 2 - ) - : '-', + value: ( + + ), className: `${cellClassNames} hidden lg:table-cell font-mono`, onlyOnDetailed: true, dataTestId: 'market-volume', }, { kind: ColumnKind.TradingMode, - value: , + value: ( + + ), className: `${cellClassNames} hidden lg:table-cell`, onlyOnDetailed: true, dataTestId: 'trading-mode-col', @@ -385,7 +358,8 @@ export const columnsPositionMarkets = ( market: Market, onSelect: (id: string) => void, openVolume?: string, - onCellClick?: OnCellClickHandler + onCellClick?: OnCellClickHandler, + activeMarketId?: string | null ) => { const candlesClose = market.candles ?.map((candle) => candle?.close) @@ -401,6 +375,7 @@ export const columnsPositionMarkets = ( } }; const candleVolume = market.candles && calcCandleVolume(market.candles); + const noUpdate = !activeMarketId || market.id !== activeMarketId; const selectMarketColumns: Column[] = [ { kind: ColumnKind.Market, @@ -428,27 +403,25 @@ export const columnsPositionMarkets = ( }, { kind: ColumnKind.LastPrice, - value: market.data?.markPrice ? ( - - ) : ( - '-' ), className: cellClassNames, onlyOnDetailed: false, }, { kind: ColumnKind.Change24, - value: candlesClose && ( - ), className: cellClassNames, @@ -525,20 +498,28 @@ export const columnsPositionMarkets = ( }, { kind: ColumnKind.Volume, - value: candleVolume - ? addDecimalsFormatNumber( - candleVolume.toString(), - market.positionDecimalPlaces, - 2 - ) - : '-', + value: ( + + ), className: `${cellClassNames} hidden lg:table-cell font-mono`, onlyOnDetailed: true, dataTestId: 'market-volume', }, { kind: ColumnKind.TradingMode, - value: , + value: ( + + ), className: `${cellClassNames} hidden lg:table-cell`, onlyOnDetailed: true, dataTestId: 'trading-mode-col', diff --git a/apps/trading/components/select-market/select-market.spec.tsx b/apps/trading/components/select-market/select-market.spec.tsx index f6ba315e4..897b3aa28 100644 --- a/apps/trading/components/select-market/select-market.spec.tsx +++ b/apps/trading/components/select-market/select-market.spec.tsx @@ -12,6 +12,7 @@ import type { MarketData, } from '@vegaprotocol/market-list'; import { MemoryRouter } from 'react-router-dom'; +import { MockedProvider } from '@apollo/client/testing'; type Market = MarketWithCandles & MarketWithData; type PartialMarket = Partial< @@ -91,6 +92,7 @@ const MARKET_B: PartialMarket = { __typename: 'Market', id: '2', decimalPlaces: 2, + positionDecimalPlaces: 0, tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, tradableInstrument: { __typename: 'TradableInstrument', @@ -158,7 +160,8 @@ describe('SelectMarket', () => { onCellClick={onCellClick} onSelect={onSelect} /> - + , + { wrapper: MockedProvider } ); expect(screen.getByText('ABCDEF')).toBeTruthy(); // name expect(screen.getByText('25.00%')).toBeTruthy(); // price change @@ -178,7 +181,8 @@ describe('SelectMarket', () => { onCellClick={onCellClick} onSelect={onSelect} /> - + , + { wrapper: MockedProvider } ); fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]); expect(onSelect).toHaveBeenCalledWith('1'); diff --git a/apps/trading/components/select-market/select-market.tsx b/apps/trading/components/select-market/select-market.tsx index ba5ab4f90..d273b7862 100644 --- a/apps/trading/components/select-market/select-market.tsx +++ b/apps/trading/components/select-market/select-market.tsx @@ -11,7 +11,7 @@ import { Popover, } from '@vegaprotocol/ui-toolkit'; import { useVegaWallet } from '@vegaprotocol/wallet'; -import { useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { columnHeaders, @@ -38,6 +38,7 @@ import { TOKEN_NEW_MARKET_PROPOSAL, useLinks, } from '@vegaprotocol/environment'; +import { useGlobalStore } from '../../stores'; type Market = MarketWithCandles & MarketWithData; @@ -87,14 +88,17 @@ export const SelectAllMarketsTableBody = ({ positions, onSelect, onCellClick, + activeMarketId, headers = columnHeaders, - tableColumns = (market) => columns(market, onSelect, onCellClick), + tableColumns = (market) => + columns(market, onSelect, onCellClick, activeMarketId), }: { markets?: Market[] | null; positions?: PositionFieldsFragment[]; title?: string; onSelect: (id: string) => void; onCellClick: OnCellClickHandler; + activeMarketId?: string | null; headers?: Column[]; tableColumns?: (market: Market, openVolume?: string) => Column[]; }) => { @@ -112,7 +116,7 @@ export const SelectAllMarketsTableBody = ({ void; onCellClick: OnCellClickHandler; }) => { + const { activeMarketId } = useGlobalStore((store) => ({ + activeMarketId: store.marketId, + })); const triggerClasses = 'sm:text-lg md:text-xl lg:text-2xl flex items-center gap-2 whitespace-nowrap hover:text-neutral-500 dark:hover:text-neutral-300 mt-1'; const { pubKey } = useVegaWallet(); const [open, setOpen] = useState(false); - const { data, loading: marketsLoading } = useMarketList(); + const { + data, + loading: marketsLoading, + reload: marketListReload, + } = useMarketList(); const variables = useMemo(() => ({ partyId: pubKey }), [pubKey]); - const { data: party, loading: positionsLoading } = useDataProvider({ + const { + data: party, + loading: positionsLoading, + reload, + } = useDataProvider({ dataProvider: positionsDataProvider, - skipUpdates: true, variables, skip: !pubKey, }); - - const onSelectMarket = (marketId: string) => { - onSelect(marketId); - setOpen(false); - }; + const onSelectMarket = useCallback( + (marketId: string) => { + onSelect(marketId); + setOpen(false); + }, + [onSelect] + ); const iconClass = open ? 'rotate-180' : ''; const markets = useMemo( @@ -172,6 +188,13 @@ export const SelectMarketPopover = ({ [data, party] ); + useEffect(() => { + if (open) { + reload(); + marketListReload(); + } + }, [open, marketListReload, reload]); + return ( @@ -221,6 +245,7 @@ export const SelectMarketPopover = ({ markets={data} onSelect={onSelectMarket} onCellClick={onCellClick} + activeMarketId={activeMarketId} /> )} diff --git a/libs/ledger/src/lib/ledger-manager.tsx b/libs/ledger/src/lib/ledger-manager.tsx index c6c719c03..aac85cf86 100644 --- a/libs/ledger/src/lib/ledger-manager.tsx +++ b/libs/ledger/src/lib/ledger-manager.tsx @@ -5,7 +5,6 @@ import { LedgerTable } from './ledger-table'; // '3ac37999796c2be3546e0c1d87daa8ec7e99d8c423969be44c2f63256c415004' type LedgerManagerProps = { partyId: string }; export const LedgerManager = ({ partyId }: LedgerManagerProps) => { - console.log('partyId', partyId); const { data, error, loading } = useLedgerEntriesDataProvider(partyId); return ( diff --git a/libs/market-list/src/lib/markets-provider.ts b/libs/market-list/src/lib/markets-provider.ts index 2b52a1f49..acc07e176 100644 --- a/libs/market-list/src/lib/markets-provider.ts +++ b/libs/market-list/src/lib/markets-provider.ts @@ -98,15 +98,15 @@ export const useMarketList = () => { interval: Schema.Interval.INTERVAL_I1H, }; }, [yesterday]); - const { data, loading, error } = useDataProvider({ + const { data, loading, error, reload } = useDataProvider({ dataProvider: marketListProvider, variables, - skipUpdates: true, }); return { data, loading, error, + reload, }; };