chore: market header live update (#1672)
* chore: market header live update * chore: market header live update * chore: market header live update - adjust some mocks * chore: market header live update - add single market query * chore: market header live update - small fixes * chore: market header live update - fix int tests * chore: market header live update - fix int tests * chore: market header live update - remove unnecessary props from query * chore: market header live update - change concept - split for small comps * chore: market header live update - small fix for mocks * chore: market header live update - fix updates throttling * chore: market header live update - improve update methods of data providers * chore: market header live update - improve update methods of data providers * chore: market header live update - improve update methods store for get rid of blinking * chore: market header live update - fix title component Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
parent
64c85b2b4a
commit
0bb2e95091
@ -3,6 +3,7 @@ import {
|
|||||||
generateSimpleMarkets,
|
generateSimpleMarkets,
|
||||||
generateMarketsCandles,
|
generateMarketsCandles,
|
||||||
generateMarketsData,
|
generateMarketsData,
|
||||||
|
generateMarket,
|
||||||
} from '../support/mocks/generate-markets';
|
} from '../support/mocks/generate-markets';
|
||||||
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
||||||
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
||||||
@ -31,6 +32,7 @@ describe('Market trade', { tags: '@smoke' }, () => {
|
|||||||
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
||||||
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
|
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
|
||||||
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
||||||
|
aliasQuery(req, 'Market', generateMarket());
|
||||||
});
|
});
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
cy.wait('@Markets').then((response) => {
|
cy.wait('@Markets').then((response) => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { Market } from '@vegaprotocol/market-list';
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
export const protoCandles = [
|
export const protoCandles = [
|
||||||
{ open: '9556163', close: '9587028', __typename: 'Candle' },
|
{ open: '9556163', close: '9587028', __typename: 'Candle' },
|
||||||
@ -123,3 +124,24 @@ export const protoMarket: Market = {
|
|||||||
},
|
},
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const singleMarket: SingleMarketFieldsFragment = {
|
||||||
|
...protoMarket,
|
||||||
|
tradableInstrument: {
|
||||||
|
...protoMarket.tradableInstrument,
|
||||||
|
instrument: {
|
||||||
|
...protoMarket.tradableInstrument.instrument,
|
||||||
|
product: {
|
||||||
|
...protoMarket.tradableInstrument.instrument.product,
|
||||||
|
settlementAsset: {
|
||||||
|
...protoMarket.tradableInstrument.instrument.product.settlementAsset,
|
||||||
|
id: 'dai-id',
|
||||||
|
name: 'DAI Name',
|
||||||
|
},
|
||||||
|
oracleSpecForTradingTermination: {
|
||||||
|
id: 'oid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@ -12,8 +12,9 @@ import type {
|
|||||||
MarketsDataQuery,
|
MarketsDataQuery,
|
||||||
MarketDataFieldsFragment,
|
MarketDataFieldsFragment,
|
||||||
} from '@vegaprotocol/market-list';
|
} from '@vegaprotocol/market-list';
|
||||||
import { protoMarket, protoCandles } from './commons';
|
import { protoMarket, protoCandles, singleMarket } from './commons';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
import type { MarketQuery } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
export const generateSimpleMarkets = (): MarketsQuery => {
|
export const generateSimpleMarkets = (): MarketsQuery => {
|
||||||
const markets: Market[] = [
|
const markets: Market[] = [
|
||||||
@ -1377,3 +1378,11 @@ export const generatePositionsMarkets = () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const generateMarket = (): MarketQuery => {
|
||||||
|
return {
|
||||||
|
market: {
|
||||||
|
...singleMarket,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
|||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import { useOrderBookData } from '@vegaprotocol/market-depth';
|
import { useOrderBookData } from '@vegaprotocol/market-depth';
|
||||||
import { marketProvider } from '@vegaprotocol/market-list';
|
import { marketProvider } from '@vegaprotocol/market-list';
|
||||||
import type { Market } from '@vegaprotocol/market-list';
|
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import {
|
import {
|
||||||
@ -22,7 +22,7 @@ const useCalculateSlippage = ({ marketId, order }: Props) => {
|
|||||||
variables,
|
variables,
|
||||||
throttleMilliseconds: 5000,
|
throttleMilliseconds: 5000,
|
||||||
});
|
});
|
||||||
const { data: market } = useDataProvider<Market, never>({
|
const { data: market } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||||
dataProvider: marketProvider,
|
dataProvider: marketProvider,
|
||||||
noUpdate: true,
|
noUpdate: true,
|
||||||
variables,
|
variables,
|
||||||
|
@ -1,40 +1,19 @@
|
|||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import {
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
AuctionTrigger,
|
|
||||||
MarketState,
|
|
||||||
MarketTradingMode,
|
|
||||||
} from '@vegaprotocol/types';
|
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
|
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
|
||||||
import type { Market } from '../../../../trading/pages/markets/__generated__/Market';
|
import type { MarketQuery } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
export const generateMarket = (override?: PartialDeep<Market>): Market => {
|
export const generateMarket = (
|
||||||
const defaultResult: Market = {
|
override?: PartialDeep<MarketQuery>
|
||||||
|
): MarketQuery => {
|
||||||
|
const defaultResult: MarketQuery = {
|
||||||
market: {
|
market: {
|
||||||
id: 'market-0',
|
id: 'market-0',
|
||||||
tradingMode: MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
tradingMode: MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||||
state: MarketState.STATE_ACTIVE,
|
state: MarketState.STATE_ACTIVE,
|
||||||
decimalPlaces: 5,
|
decimalPlaces: 5,
|
||||||
positionDecimalPlaces: 0,
|
positionDecimalPlaces: 0,
|
||||||
data: {
|
|
||||||
market: {
|
|
||||||
id: 'market-0',
|
|
||||||
__typename: 'Market',
|
|
||||||
},
|
|
||||||
auctionStart: '2022-08-12T11:13:47.611014117Z',
|
|
||||||
auctionEnd: '2022-08-16T09:08:23.611014117Z',
|
|
||||||
markPrice: '13739109',
|
|
||||||
indicativeVolume: '2316',
|
|
||||||
indicativePrice: '88470230',
|
|
||||||
suppliedStake: '79481836527',
|
|
||||||
targetStake: '97284519014',
|
|
||||||
bestBidVolume: '244',
|
|
||||||
bestOfferVolume: '100',
|
|
||||||
bestStaticBidVolume: '482',
|
|
||||||
bestStaticOfferVolume: '2188',
|
|
||||||
trigger: AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY,
|
|
||||||
__typename: 'MarketData',
|
|
||||||
},
|
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
id: 'BTCUSD.MF21',
|
id: 'BTCUSD.MF21',
|
||||||
@ -75,36 +54,15 @@ export const generateMarket = (override?: PartialDeep<Market>): Market => {
|
|||||||
close: null,
|
close: null,
|
||||||
__typename: 'MarketTimestamps',
|
__typename: 'MarketTimestamps',
|
||||||
},
|
},
|
||||||
depth: {
|
fees: {
|
||||||
__typename: 'MarketDepth',
|
__typename: 'Fees',
|
||||||
lastTrade: {
|
factors: {
|
||||||
__typename: 'Trade',
|
__typename: 'FeeFactors',
|
||||||
price: '88470230',
|
makerFee: '',
|
||||||
|
infrastructureFee: '',
|
||||||
|
liquidityFee: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
candlesConnection: {
|
|
||||||
__typename: 'CandleDataConnection',
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'CandleEdge',
|
|
||||||
node: {
|
|
||||||
open: '2095312844',
|
|
||||||
close: '2090090607',
|
|
||||||
volume: '4847',
|
|
||||||
__typename: 'Candle',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
__typename: 'CandleEdge',
|
|
||||||
node: {
|
|
||||||
open: '2090090000',
|
|
||||||
close: '2090090607',
|
|
||||||
volume: '4847',
|
|
||||||
__typename: 'Candle',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
1
apps/trading/components/constants.ts
Normal file
1
apps/trading/components/constants.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const DEBOUNCE_UPDATE_TIME = 500;
|
1
apps/trading/components/last-24h-price-change/index.ts
Normal file
1
apps/trading/components/last-24h-price-change/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './last-24h-price-change';
|
@ -0,0 +1,85 @@
|
|||||||
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
import throttle from 'lodash/throttle';
|
||||||
|
import { t, useDataProvider, useYesterday } from '@vegaprotocol/react-helpers';
|
||||||
|
import { PriceCellChange } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Interval } from '@vegaprotocol/types';
|
||||||
|
import type { CandleClose } from '@vegaprotocol/types';
|
||||||
|
import type {
|
||||||
|
SingleMarketFieldsFragment,
|
||||||
|
Candle,
|
||||||
|
} from '@vegaprotocol/market-list';
|
||||||
|
import {
|
||||||
|
marketCandlesProvider,
|
||||||
|
marketProvider,
|
||||||
|
} from '@vegaprotocol/market-list';
|
||||||
|
import { HeaderStat } from '../header';
|
||||||
|
import * as constants from '../constants';
|
||||||
|
|
||||||
|
export const Last24hPriceChange = ({ marketId }: { marketId: string }) => {
|
||||||
|
const [candlesClose, setCandlesClose] = useState<string[]>([]);
|
||||||
|
const yesterday = useYesterday();
|
||||||
|
// Cache timestamp for yesterday to prevent full unmount of market page when
|
||||||
|
// a rerender occurs
|
||||||
|
const yTimestamp = useMemo(() => {
|
||||||
|
return new Date(yesterday).toISOString();
|
||||||
|
}, [yesterday]);
|
||||||
|
|
||||||
|
const marketVariables = useMemo(
|
||||||
|
() => ({
|
||||||
|
marketId: marketId,
|
||||||
|
}),
|
||||||
|
[marketId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const variables = useMemo(
|
||||||
|
() => ({
|
||||||
|
marketId: marketId,
|
||||||
|
interval: Interval.INTERVAL_I1H,
|
||||||
|
since: yTimestamp,
|
||||||
|
}),
|
||||||
|
[marketId, yTimestamp]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data, error } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||||
|
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);
|
||||||
|
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||||
|
).current;
|
||||||
|
const update = useCallback(
|
||||||
|
({ data }: { data: Candle[] }) => {
|
||||||
|
throttledSetCandles(data);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[throttledSetCandles]
|
||||||
|
);
|
||||||
|
|
||||||
|
useDataProvider<Candle[], Candle>({
|
||||||
|
dataProvider: marketCandlesProvider,
|
||||||
|
update,
|
||||||
|
variables,
|
||||||
|
skip: !marketId || !data,
|
||||||
|
updateOnInit: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderStat heading={t('Change (24h)')}>
|
||||||
|
{!error && data?.decimalPlaces ? (
|
||||||
|
<PriceCellChange
|
||||||
|
candles={candlesClose}
|
||||||
|
decimalPlaces={data.decimalPlaces}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
|
</HeaderStat>
|
||||||
|
);
|
||||||
|
};
|
1
apps/trading/components/market-mark-price/index.ts
Normal file
1
apps/trading/components/market-mark-price/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './market-mark-price';
|
@ -0,0 +1,60 @@
|
|||||||
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
import throttle from 'lodash/throttle';
|
||||||
|
import {
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
t,
|
||||||
|
useDataProvider,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import type {
|
||||||
|
MarketData,
|
||||||
|
MarketDataUpdateFieldsFragment,
|
||||||
|
SingleMarketFieldsFragment,
|
||||||
|
} from '@vegaprotocol/market-list';
|
||||||
|
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||||
|
import { HeaderStat } from '../header';
|
||||||
|
import * as constants from '../constants';
|
||||||
|
|
||||||
|
export const MarketMarkPrice = ({ marketId }: { marketId: string }) => {
|
||||||
|
const [marketPrice, setMarketPrice] = useState<string | null>(null);
|
||||||
|
const variables = useMemo(
|
||||||
|
() => ({
|
||||||
|
marketId: marketId,
|
||||||
|
}),
|
||||||
|
[marketId]
|
||||||
|
);
|
||||||
|
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||||
|
dataProvider: marketProvider,
|
||||||
|
variables,
|
||||||
|
skip: !marketId,
|
||||||
|
});
|
||||||
|
const throttledSetMarketPrice = useRef(
|
||||||
|
throttle((price: string) => {
|
||||||
|
setMarketPrice(price);
|
||||||
|
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||||
|
).current;
|
||||||
|
const update = useCallback(
|
||||||
|
({ data: marketData }: { data: MarketData }) => {
|
||||||
|
throttledSetMarketPrice(
|
||||||
|
marketData.markPrice && data?.decimalPlaces
|
||||||
|
? addDecimalsFormatNumber(marketData.markPrice, data.decimalPlaces)
|
||||||
|
: '-'
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[data?.decimalPlaces, throttledSetMarketPrice]
|
||||||
|
);
|
||||||
|
|
||||||
|
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||||
|
dataProvider: marketDataProvider,
|
||||||
|
update,
|
||||||
|
variables,
|
||||||
|
skip: !marketId || !data,
|
||||||
|
updateOnInit: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderStat heading={t('Price')}>
|
||||||
|
<div data-testid="mark-price">{marketPrice}</div>
|
||||||
|
</HeaderStat>
|
||||||
|
);
|
||||||
|
};
|
1
apps/trading/components/market-trading-mode/index.ts
Normal file
1
apps/trading/components/market-trading-mode/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './market-trading-mode';
|
@ -0,0 +1,89 @@
|
|||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
|
import { compileGridData, TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
||||||
|
import type { Schema as Types } from '@vegaprotocol/types';
|
||||||
|
import {
|
||||||
|
AuctionTrigger,
|
||||||
|
AuctionTriggerMapping,
|
||||||
|
MarketTradingModeMapping,
|
||||||
|
MarketTradingMode,
|
||||||
|
} from '@vegaprotocol/types';
|
||||||
|
import type {
|
||||||
|
MarketData,
|
||||||
|
MarketDataUpdateFieldsFragment,
|
||||||
|
SingleMarketFieldsFragment,
|
||||||
|
} from '@vegaprotocol/market-list';
|
||||||
|
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||||
|
import { HeaderStat } from '../header';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
marketId: string;
|
||||||
|
onSelect: (marketId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TradingModeMarket = Omit<DealTicketMarketFragment, 'depth'>;
|
||||||
|
|
||||||
|
export const MarketTradingModeComponent = ({ marketId, onSelect }: Props) => {
|
||||||
|
const [tradingMode, setTradingMode] =
|
||||||
|
useState<Types.MarketTradingMode | null>(null);
|
||||||
|
const [trigger, setTrigger] = useState<Types.AuctionTrigger | null>(null);
|
||||||
|
const [market, setMarket] = useState<TradingModeMarket | null>(null);
|
||||||
|
const variables = useMemo(
|
||||||
|
() => ({
|
||||||
|
marketId: marketId,
|
||||||
|
}),
|
||||||
|
[marketId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||||
|
dataProvider: marketProvider,
|
||||||
|
variables,
|
||||||
|
skip: !marketId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const update = useCallback(
|
||||||
|
({ data: marketData }: { data: MarketData }) => {
|
||||||
|
setTradingMode(marketData.marketTradingMode);
|
||||||
|
setTrigger(marketData.trigger);
|
||||||
|
setMarket({
|
||||||
|
...data,
|
||||||
|
data: marketData,
|
||||||
|
} as TradingModeMarket);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[data]
|
||||||
|
);
|
||||||
|
|
||||||
|
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||||
|
dataProvider: marketDataProvider,
|
||||||
|
update,
|
||||||
|
variables,
|
||||||
|
skip: !marketId || !data,
|
||||||
|
updateOnInit: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderStat
|
||||||
|
heading={t('Trading mode')}
|
||||||
|
description={
|
||||||
|
market && (
|
||||||
|
<TradingModeTooltip
|
||||||
|
tradingMode={tradingMode}
|
||||||
|
trigger={trigger}
|
||||||
|
compiledGrid={compileGridData(market, onSelect)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div data-testid="trading-mode">
|
||||||
|
{tradingMode === MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
|
trigger &&
|
||||||
|
trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
||||||
|
? `${MarketTradingModeMapping[tradingMode]}
|
||||||
|
- ${AuctionTriggerMapping[trigger]}`
|
||||||
|
: MarketTradingModeMapping[tradingMode as Types.MarketTradingMode]}
|
||||||
|
</div>
|
||||||
|
</HeaderStat>
|
||||||
|
);
|
||||||
|
};
|
1
apps/trading/components/market-volume/index.ts
Normal file
1
apps/trading/components/market-volume/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './market-volume';
|
64
apps/trading/components/market-volume/market-volume.tsx
Normal file
64
apps/trading/components/market-volume/market-volume.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
import throttle from 'lodash/throttle';
|
||||||
|
import {
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
t,
|
||||||
|
useDataProvider,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import type {
|
||||||
|
MarketData,
|
||||||
|
MarketDataUpdateFieldsFragment,
|
||||||
|
SingleMarketFieldsFragment,
|
||||||
|
} from '@vegaprotocol/market-list';
|
||||||
|
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||||
|
import { HeaderStat } from '../header';
|
||||||
|
import * as constants from '../constants';
|
||||||
|
|
||||||
|
export const MarketVolume = ({ marketId }: { marketId: string }) => {
|
||||||
|
const [marketVolume, setMarketVolume] = useState<string>('-');
|
||||||
|
const variables = useMemo(
|
||||||
|
() => ({
|
||||||
|
marketId: marketId,
|
||||||
|
}),
|
||||||
|
[marketId]
|
||||||
|
);
|
||||||
|
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||||
|
dataProvider: marketProvider,
|
||||||
|
variables,
|
||||||
|
skip: !marketId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const throttledSetMarketVolume = useRef(
|
||||||
|
throttle((volume: string) => {
|
||||||
|
setMarketVolume(volume);
|
||||||
|
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||||
|
).current;
|
||||||
|
const update = useCallback(
|
||||||
|
({ data: marketData }: { data: MarketData }) => {
|
||||||
|
throttledSetMarketVolume(
|
||||||
|
marketData.indicativeVolume && data?.positionDecimalPlaces !== undefined
|
||||||
|
? addDecimalsFormatNumber(
|
||||||
|
marketData.indicativeVolume,
|
||||||
|
data.positionDecimalPlaces
|
||||||
|
)
|
||||||
|
: '-'
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[data?.positionDecimalPlaces, throttledSetMarketVolume]
|
||||||
|
);
|
||||||
|
|
||||||
|
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||||
|
dataProvider: marketDataProvider,
|
||||||
|
update,
|
||||||
|
variables,
|
||||||
|
skip: !marketId || !data,
|
||||||
|
updateOnInit: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderStat heading={t('Volume')}>
|
||||||
|
<div data-testid="trading-volume">{marketVolume}</div>
|
||||||
|
</HeaderStat>
|
||||||
|
);
|
||||||
|
};
|
@ -22,14 +22,10 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
||||||
|
|
||||||
function AppBody({ Component, pageProps }: AppProps) {
|
const Title = () => {
|
||||||
const { connectDialog, pageTitle, update } = useGlobalStore((store) => ({
|
const { pageTitle } = useGlobalStore((store) => ({
|
||||||
connectDialog: store.connectDialog,
|
|
||||||
pageTitle: store.pageTitle,
|
pageTitle: store.pageTitle,
|
||||||
update: store.update,
|
|
||||||
}));
|
}));
|
||||||
const { isOpen, symbol, trigger, setOpen } = useAssetDetailsDialogStore();
|
|
||||||
const [theme, toggleTheme] = useThemeSwitcher();
|
|
||||||
|
|
||||||
const { VEGA_ENV } = useEnvironment();
|
const { VEGA_ENV } = useEnvironment();
|
||||||
const networkName = envTriggerMapping[VEGA_ENV];
|
const networkName = envTriggerMapping[VEGA_ENV];
|
||||||
@ -39,11 +35,21 @@ function AppBody({ Component, pageProps }: AppProps) {
|
|||||||
if (networkName) return `${pageTitle} [${networkName}]`;
|
if (networkName) return `${pageTitle} [${networkName}]`;
|
||||||
return pageTitle;
|
return pageTitle;
|
||||||
}, [pageTitle, networkName]);
|
}, [pageTitle, networkName]);
|
||||||
|
return <title>{title}</title>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function AppBody({ Component, pageProps }: AppProps) {
|
||||||
|
const { connectDialog, update } = useGlobalStore((store) => ({
|
||||||
|
connectDialog: store.connectDialog,
|
||||||
|
update: store.update,
|
||||||
|
}));
|
||||||
|
const { isOpen, symbol, trigger, setOpen } = useAssetDetailsDialogStore();
|
||||||
|
const [theme, toggleTheme] = useThemeSwitcher();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={theme}>
|
<ThemeContext.Provider value={theme}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title}</title>
|
<Title />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="h-full relative dark:bg-black dark:text-white z-0 grid grid-rows-[min-content,1fr,min-content]">
|
<div className="h-full relative dark:bg-black dark:text-white z-0 grid grid-rows-[min-content,1fr,min-content]">
|
||||||
<AppLoader>
|
<AppLoader>
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
query Market($marketId: ID!, $interval: Interval!, $since: String!) {
|
|
||||||
market(id: $marketId) {
|
|
||||||
id
|
|
||||||
tradingMode
|
|
||||||
state
|
|
||||||
decimalPlaces
|
|
||||||
positionDecimalPlaces
|
|
||||||
data {
|
|
||||||
market {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
auctionStart
|
|
||||||
auctionEnd
|
|
||||||
markPrice
|
|
||||||
indicativeVolume
|
|
||||||
indicativePrice
|
|
||||||
suppliedStake
|
|
||||||
targetStake
|
|
||||||
bestBidVolume
|
|
||||||
bestOfferVolume
|
|
||||||
bestStaticBidVolume
|
|
||||||
bestStaticOfferVolume
|
|
||||||
trigger
|
|
||||||
}
|
|
||||||
tradableInstrument {
|
|
||||||
instrument {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
code
|
|
||||||
metadata {
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
... on Future {
|
|
||||||
oracleSpecForTradingTermination {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
quoteName
|
|
||||||
settlementAsset {
|
|
||||||
id
|
|
||||||
symbol
|
|
||||||
name
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
marketTimestamps {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
}
|
|
||||||
depth {
|
|
||||||
lastTrade {
|
|
||||||
price
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candlesConnection(interval: $interval, since: $since) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
volume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,158 +1,114 @@
|
|||||||
import { gql, useQuery } from '@apollo/client';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import {
|
import {
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
t,
|
t,
|
||||||
titlefy,
|
titlefy,
|
||||||
useYesterday,
|
useDataProvider,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { Interval } from '@vegaprotocol/types';
|
|
||||||
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import debounce from 'lodash/debounce';
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import type {
|
||||||
|
SingleMarketFieldsFragment,
|
||||||
|
MarketData,
|
||||||
|
Candle,
|
||||||
|
MarketDataUpdateFieldsFragment,
|
||||||
|
} from '@vegaprotocol/market-list';
|
||||||
|
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
|
||||||
import { useGlobalStore } from '../../stores';
|
import { useGlobalStore } from '../../stores';
|
||||||
import { TradeGrid, TradePanels } from './trade-grid';
|
import { TradeGrid, TradePanels } from './trade-grid';
|
||||||
import type { Market, MarketVariables } from './__generated__/Market';
|
|
||||||
import { ColumnKind, SelectMarketDialog } from '../../components/select-market';
|
import { ColumnKind, SelectMarketDialog } from '../../components/select-market';
|
||||||
|
|
||||||
// Top level page query
|
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||||
const MARKET_QUERY = gql`
|
return markPrice && decimalPlaces
|
||||||
query Market($marketId: ID!, $interval: Interval!, $since: String!) {
|
? addDecimalsFormatNumber(markPrice, decimalPlaces)
|
||||||
market(id: $marketId) {
|
: '-';
|
||||||
id
|
};
|
||||||
tradingMode
|
|
||||||
state
|
|
||||||
decimalPlaces
|
|
||||||
positionDecimalPlaces
|
|
||||||
data {
|
|
||||||
market {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
auctionStart
|
|
||||||
auctionEnd
|
|
||||||
markPrice
|
|
||||||
indicativeVolume
|
|
||||||
indicativePrice
|
|
||||||
suppliedStake
|
|
||||||
targetStake
|
|
||||||
bestBidVolume
|
|
||||||
bestOfferVolume
|
|
||||||
bestStaticBidVolume
|
|
||||||
bestStaticOfferVolume
|
|
||||||
trigger
|
|
||||||
}
|
|
||||||
tradableInstrument {
|
|
||||||
instrument {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
code
|
|
||||||
metadata {
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
... on Future {
|
|
||||||
oracleSpecForTradingTermination {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
quoteName
|
|
||||||
settlementAsset {
|
|
||||||
id
|
|
||||||
symbol
|
|
||||||
name
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
marketTimestamps {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
}
|
|
||||||
depth {
|
|
||||||
lastTrade {
|
|
||||||
price
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candlesConnection(interval: $interval, since: $since) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
volume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const MarketPage = ({ id }: { id?: string }) => {
|
export interface SingleMarketData extends SingleMarketFieldsFragment {
|
||||||
|
candles: Candle[];
|
||||||
|
data: MarketData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarketPage = ({
|
||||||
|
id,
|
||||||
|
marketId: mid,
|
||||||
|
}: {
|
||||||
|
id?: string;
|
||||||
|
marketId?: string;
|
||||||
|
}) => {
|
||||||
const { query, push } = useRouter();
|
const { query, push } = useRouter();
|
||||||
const { w } = useWindowSize();
|
const { w } = useWindowSize();
|
||||||
const { landingDialog, riskNoticeDialog, update } = useGlobalStore(
|
const {
|
||||||
(store) => ({
|
landingDialog,
|
||||||
|
riskNoticeDialog,
|
||||||
|
update,
|
||||||
|
updateTitle,
|
||||||
|
updateMarketId,
|
||||||
|
} = useGlobalStore((store) => ({
|
||||||
landingDialog: store.landingDialog,
|
landingDialog: store.landingDialog,
|
||||||
riskNoticeDialog: store.riskNoticeDialog,
|
riskNoticeDialog: store.riskNoticeDialog,
|
||||||
update: store.update,
|
update: store.update,
|
||||||
})
|
updateTitle: store.updateTitle,
|
||||||
);
|
updateMarketId: store.updateMarketId,
|
||||||
const { update: updateStore } = useGlobalStore((store) => ({
|
|
||||||
update: store.update,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||||
|
|
||||||
// Default to first marketId query item if found
|
// Default to first marketId query item if found
|
||||||
const marketId =
|
const marketId =
|
||||||
id || (Array.isArray(query.marketId) ? query.marketId[0] : query.marketId);
|
id || (Array.isArray(query.marketId) ? query.marketId[0] : query.marketId);
|
||||||
|
|
||||||
const onSelect = (id: string) => {
|
const onSelect = useCallback(
|
||||||
|
(id: string) => {
|
||||||
if (id && id !== marketId) {
|
if (id && id !== marketId) {
|
||||||
updateStore({ marketId: id });
|
updateMarketId(id);
|
||||||
push(`/markets/${id}`);
|
push(`/markets/${id}`);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
[marketId, updateMarketId, push]
|
||||||
const yesterday = useYesterday();
|
);
|
||||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
|
||||||
// a rerender occurs
|
|
||||||
const yTimestamp = useMemo(() => {
|
|
||||||
return new Date(yesterday).toISOString();
|
|
||||||
}, [yesterday]);
|
|
||||||
|
|
||||||
const variables = useMemo(
|
const variables = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
marketId: marketId || '',
|
marketId: marketId || '',
|
||||||
interval: Interval.INTERVAL_I1H,
|
|
||||||
since: yTimestamp,
|
|
||||||
}),
|
}),
|
||||||
[marketId, yTimestamp]
|
[marketId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, error, loading } = useQuery<Market, MarketVariables>(
|
const { data, error, loading } = useDataProvider<
|
||||||
MARKET_QUERY,
|
SingleMarketFieldsFragment,
|
||||||
{
|
never
|
||||||
|
>({
|
||||||
|
dataProvider: marketProvider,
|
||||||
variables,
|
variables,
|
||||||
fetchPolicy: 'network-only',
|
skip: !marketId,
|
||||||
errorPolicy: 'ignore',
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const updateProvider = useCallback(
|
||||||
const marketName = data?.market?.tradableInstrument.instrument.name;
|
({ data: marketData }: { data: MarketData }) => {
|
||||||
const marketPrice =
|
const marketName = data?.tradableInstrument.instrument.name;
|
||||||
data?.market && data?.market?.data
|
const marketPrice = calculatePrice(
|
||||||
? addDecimalsFormatNumber(
|
marketData.markPrice,
|
||||||
data.market.data.markPrice,
|
data?.decimalPlaces
|
||||||
data.market.decimalPlaces
|
);
|
||||||
)
|
|
||||||
: null;
|
|
||||||
if (marketName) {
|
if (marketName) {
|
||||||
const pageTitle = titlefy([marketName, marketPrice]);
|
const pageTitle = titlefy([marketName, marketPrice]);
|
||||||
update({ pageTitle });
|
updateTitle(pageTitle);
|
||||||
}
|
}
|
||||||
}, [data, update]);
|
return true;
|
||||||
|
},
|
||||||
|
[updateTitle, data?.tradableInstrument.instrument.name, data?.decimalPlaces]
|
||||||
|
);
|
||||||
|
|
||||||
|
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||||
|
dataProvider: marketDataProvider,
|
||||||
|
update: updateProvider,
|
||||||
|
variables,
|
||||||
|
skip: !marketId || !data,
|
||||||
|
updateOnInit: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (!marketId) {
|
if (!marketId) {
|
||||||
return (
|
return (
|
||||||
@ -163,20 +119,20 @@ const MarketPage = ({ id }: { id?: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer<Market>
|
<AsyncRenderer<SingleMarketFieldsFragment>
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
data={data}
|
data={data || undefined}
|
||||||
render={({ market }) => {
|
render={(data) => {
|
||||||
if (!market) {
|
if (!data) {
|
||||||
return <Splash>{t('Market not found')}</Splash>;
|
return <Splash>{t('Market not found')}</Splash>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{w > 960 ? (
|
{w > 960 ? (
|
||||||
<TradeGrid market={market} onSelect={onSelect} />
|
<TradeGrid market={data} onSelect={onSelect} />
|
||||||
) : (
|
) : (
|
||||||
<TradePanels market={market} onSelect={onSelect} />
|
<TradePanels market={data} onSelect={onSelect} />
|
||||||
)}
|
)}
|
||||||
<SelectMarketDialog
|
<SelectMarketDialog
|
||||||
dialogOpen={landingDialog && !riskNoticeDialog}
|
dialogOpen={landingDialog && !riskNoticeDialog}
|
||||||
|
291
apps/trading/pages/markets/__generated__/Market.ts
generated
291
apps/trading/pages/markets/__generated__/Market.ts
generated
@ -1,291 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @generated
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
|
|
||||||
import { Interval, MarketTradingMode, MarketState, AuctionTrigger } from "@vegaprotocol/types";
|
|
||||||
|
|
||||||
// ====================================================
|
|
||||||
// GraphQL query operation: Market
|
|
||||||
// ====================================================
|
|
||||||
|
|
||||||
export interface Market_market_data_market {
|
|
||||||
__typename: "Market";
|
|
||||||
/**
|
|
||||||
* Market ID
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_data {
|
|
||||||
__typename: "MarketData";
|
|
||||||
/**
|
|
||||||
* Market of the associated mark price
|
|
||||||
*/
|
|
||||||
market: Market_market_data_market;
|
|
||||||
/**
|
|
||||||
* RFC3339Nano time at which the next auction will start (null if none is scheduled)
|
|
||||||
*/
|
|
||||||
auctionStart: string | null;
|
|
||||||
/**
|
|
||||||
* RFC3339Nano time at which the auction will stop (null if not in auction mode)
|
|
||||||
*/
|
|
||||||
auctionEnd: string | null;
|
|
||||||
/**
|
|
||||||
* The mark price (an unsigned integer)
|
|
||||||
*/
|
|
||||||
markPrice: string;
|
|
||||||
/**
|
|
||||||
* Indicative volume if the auction ended now, 0 if not in auction mode
|
|
||||||
*/
|
|
||||||
indicativeVolume: string;
|
|
||||||
/**
|
|
||||||
* Indicative price if the auction ended now, 0 if not in auction mode
|
|
||||||
*/
|
|
||||||
indicativePrice: string;
|
|
||||||
/**
|
|
||||||
* The supplied stake for the market
|
|
||||||
*/
|
|
||||||
suppliedStake: string | null;
|
|
||||||
/**
|
|
||||||
* The amount of stake targeted for this market
|
|
||||||
*/
|
|
||||||
targetStake: string | null;
|
|
||||||
/**
|
|
||||||
* The aggregated volume being bid at the best bid price.
|
|
||||||
*/
|
|
||||||
bestBidVolume: string;
|
|
||||||
/**
|
|
||||||
* The aggregated volume being offered at the best offer price.
|
|
||||||
*/
|
|
||||||
bestOfferVolume: string;
|
|
||||||
/**
|
|
||||||
* The aggregated volume being offered at the best static bid price, excluding pegged orders
|
|
||||||
*/
|
|
||||||
bestStaticBidVolume: string;
|
|
||||||
/**
|
|
||||||
* The aggregated volume being offered at the best static offer price, excluding pegged orders.
|
|
||||||
*/
|
|
||||||
bestStaticOfferVolume: string;
|
|
||||||
/**
|
|
||||||
* What triggered an auction (if an auction was started)
|
|
||||||
*/
|
|
||||||
trigger: AuctionTrigger;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument_instrument_metadata {
|
|
||||||
__typename: "InstrumentMetadata";
|
|
||||||
/**
|
|
||||||
* An arbitrary list of tags to associated to associate to the Instrument (string list)
|
|
||||||
*/
|
|
||||||
tags: string[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument_instrument_product_oracleSpecForTradingTermination {
|
|
||||||
__typename: "OracleSpec";
|
|
||||||
/**
|
|
||||||
* ID is a hash generated from the OracleSpec data.
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument_instrument_product_settlementAsset {
|
|
||||||
__typename: "Asset";
|
|
||||||
/**
|
|
||||||
* The ID of the asset
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* The symbol of the asset (e.g: GBP)
|
|
||||||
*/
|
|
||||||
symbol: string;
|
|
||||||
/**
|
|
||||||
* The full name of the asset (e.g: Great British Pound)
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
|
|
||||||
*/
|
|
||||||
decimals: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument_instrument_product {
|
|
||||||
__typename: "Future";
|
|
||||||
/**
|
|
||||||
* The oracle spec describing the oracle data of interest for trading termination.
|
|
||||||
*/
|
|
||||||
oracleSpecForTradingTermination: Market_market_tradableInstrument_instrument_product_oracleSpecForTradingTermination;
|
|
||||||
/**
|
|
||||||
* String representing the quote (e.g. BTCUSD -> USD is quote)
|
|
||||||
*/
|
|
||||||
quoteName: string;
|
|
||||||
/**
|
|
||||||
* The name of the asset (string)
|
|
||||||
*/
|
|
||||||
settlementAsset: Market_market_tradableInstrument_instrument_product_settlementAsset;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument_instrument {
|
|
||||||
__typename: "Instrument";
|
|
||||||
/**
|
|
||||||
* Uniquely identify an instrument across all instruments available on Vega (string)
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Full and fairly descriptive name for the instrument
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
|
||||||
*/
|
|
||||||
code: string;
|
|
||||||
/**
|
|
||||||
* Metadata for this instrument
|
|
||||||
*/
|
|
||||||
metadata: Market_market_tradableInstrument_instrument_metadata;
|
|
||||||
/**
|
|
||||||
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
|
|
||||||
*/
|
|
||||||
product: Market_market_tradableInstrument_instrument_product;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument {
|
|
||||||
__typename: "TradableInstrument";
|
|
||||||
/**
|
|
||||||
* An instance of, or reference to, a fully specified instrument.
|
|
||||||
*/
|
|
||||||
instrument: Market_market_tradableInstrument_instrument;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_marketTimestamps {
|
|
||||||
__typename: "MarketTimestamps";
|
|
||||||
/**
|
|
||||||
* Time when the market is open and ready to accept trades
|
|
||||||
*/
|
|
||||||
open: string | null;
|
|
||||||
/**
|
|
||||||
* Time when the market is closed
|
|
||||||
*/
|
|
||||||
close: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_depth_lastTrade {
|
|
||||||
__typename: "Trade";
|
|
||||||
/**
|
|
||||||
* The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
|
|
||||||
*/
|
|
||||||
price: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_depth {
|
|
||||||
__typename: "MarketDepth";
|
|
||||||
/**
|
|
||||||
* Last trade for the given market (if available)
|
|
||||||
*/
|
|
||||||
lastTrade: Market_market_depth_lastTrade | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_candlesConnection_edges_node {
|
|
||||||
__typename: "Candle";
|
|
||||||
/**
|
|
||||||
* Open price (uint64)
|
|
||||||
*/
|
|
||||||
open: string;
|
|
||||||
/**
|
|
||||||
* Close price (uint64)
|
|
||||||
*/
|
|
||||||
close: string;
|
|
||||||
/**
|
|
||||||
* Volume price (uint64)
|
|
||||||
*/
|
|
||||||
volume: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_candlesConnection_edges {
|
|
||||||
__typename: "CandleEdge";
|
|
||||||
/**
|
|
||||||
* The candle
|
|
||||||
*/
|
|
||||||
node: Market_market_candlesConnection_edges_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market_candlesConnection {
|
|
||||||
__typename: "CandleDataConnection";
|
|
||||||
/**
|
|
||||||
* The candles
|
|
||||||
*/
|
|
||||||
edges: (Market_market_candlesConnection_edges | null)[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market_market {
|
|
||||||
__typename: "Market";
|
|
||||||
/**
|
|
||||||
* Market ID
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Current mode of execution of the market
|
|
||||||
*/
|
|
||||||
tradingMode: MarketTradingMode;
|
|
||||||
/**
|
|
||||||
* Current state of the market
|
|
||||||
*/
|
|
||||||
state: MarketState;
|
|
||||||
/**
|
|
||||||
* The number of decimal places that an integer must be shifted by in order to get a correct
|
|
||||||
* number denominated in the currency of the market. (uint64)
|
|
||||||
*
|
|
||||||
* Examples:
|
|
||||||
* Currency Balance decimalPlaces Real Balance
|
|
||||||
* GBP 100 0 GBP 100
|
|
||||||
* GBP 100 2 GBP 1.00
|
|
||||||
* GBP 100 4 GBP 0.01
|
|
||||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
|
||||||
*
|
|
||||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
|
||||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
|
||||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
|
||||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
|
||||||
*/
|
|
||||||
decimalPlaces: number;
|
|
||||||
/**
|
|
||||||
* The number of decimal places that an integer must be shifted in order to get a correct size (uint64).
|
|
||||||
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
|
|
||||||
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
|
|
||||||
* This sets how big the smallest order / position on the market can be.
|
|
||||||
*/
|
|
||||||
positionDecimalPlaces: number;
|
|
||||||
/**
|
|
||||||
* marketData for the given market
|
|
||||||
*/
|
|
||||||
data: Market_market_data | null;
|
|
||||||
/**
|
|
||||||
* An instance of, or reference to, a tradable instrument.
|
|
||||||
*/
|
|
||||||
tradableInstrument: Market_market_tradableInstrument;
|
|
||||||
/**
|
|
||||||
* Timestamps for state changes in the market
|
|
||||||
*/
|
|
||||||
marketTimestamps: Market_market_marketTimestamps;
|
|
||||||
/**
|
|
||||||
* Current depth on the order book for this market
|
|
||||||
*/
|
|
||||||
depth: Market_market_depth;
|
|
||||||
/**
|
|
||||||
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by parameters using cursor based pagination
|
|
||||||
*/
|
|
||||||
candlesConnection: Market_market_candlesConnection | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Market {
|
|
||||||
/**
|
|
||||||
* An instrument that is trading on the Vega network
|
|
||||||
*/
|
|
||||||
market: Market_market | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MarketVariables {
|
|
||||||
marketId: string;
|
|
||||||
interval: Interval;
|
|
||||||
since: string;
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
import { Schema as Types } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
import { gql } from '@apollo/client';
|
|
||||||
import * as Apollo from '@apollo/client';
|
|
||||||
const defaultOptions = {} as const;
|
|
||||||
export type MarketQueryVariables = Types.Exact<{
|
|
||||||
marketId: Types.Scalars['ID'];
|
|
||||||
interval: Types.Interval;
|
|
||||||
since: Types.Scalars['String'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type MarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, tradingMode: Types.MarketTradingMode, state: Types.MarketState, decimalPlaces: number, positionDecimalPlaces: number, data?: { __typename?: 'MarketData', auctionStart?: string | null, auctionEnd?: string | null, markPrice: string, indicativeVolume: string, indicativePrice: string, suppliedStake?: string | null, targetStake?: string | null, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, oracleSpecForTradingTermination: { __typename?: 'OracleSpec', id: string }, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open?: string | null, close?: string | null }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', open: string, close: string, volume: string } } | null> | null } | null } | null };
|
|
||||||
|
|
||||||
|
|
||||||
export const MarketDocument = gql`
|
|
||||||
query Market($marketId: ID!, $interval: Interval!, $since: String!) {
|
|
||||||
market(id: $marketId) {
|
|
||||||
id
|
|
||||||
tradingMode
|
|
||||||
state
|
|
||||||
decimalPlaces
|
|
||||||
positionDecimalPlaces
|
|
||||||
data {
|
|
||||||
market {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
auctionStart
|
|
||||||
auctionEnd
|
|
||||||
markPrice
|
|
||||||
indicativeVolume
|
|
||||||
indicativePrice
|
|
||||||
suppliedStake
|
|
||||||
targetStake
|
|
||||||
bestBidVolume
|
|
||||||
bestOfferVolume
|
|
||||||
bestStaticBidVolume
|
|
||||||
bestStaticOfferVolume
|
|
||||||
trigger
|
|
||||||
}
|
|
||||||
tradableInstrument {
|
|
||||||
instrument {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
code
|
|
||||||
metadata {
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
... on Future {
|
|
||||||
oracleSpecForTradingTermination {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
quoteName
|
|
||||||
settlementAsset {
|
|
||||||
id
|
|
||||||
symbol
|
|
||||||
name
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
marketTimestamps {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
}
|
|
||||||
depth {
|
|
||||||
lastTrade {
|
|
||||||
price
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candlesConnection(interval: $interval, since: $since) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
volume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useMarketQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useMarketQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useMarketQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
|
||||||
* you can use to render your UI.
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const { data, loading, error } = useMarketQuery({
|
|
||||||
* variables: {
|
|
||||||
* marketId: // value for 'marketId'
|
|
||||||
* interval: // value for 'interval'
|
|
||||||
* since: // value for 'since'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useMarketQuery(baseOptions: Apollo.QueryHookOptions<MarketQuery, MarketQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useQuery<MarketQuery, MarketQueryVariables>(MarketDocument, options);
|
|
||||||
}
|
|
||||||
export function useMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketQuery, MarketQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useLazyQuery<MarketQuery, MarketQueryVariables>(MarketDocument, options);
|
|
||||||
}
|
|
||||||
export type MarketQueryHookResult = ReturnType<typeof useMarketQuery>;
|
|
||||||
export type MarketLazyQueryHookResult = ReturnType<typeof useMarketLazyQuery>;
|
|
||||||
export type MarketQueryResult = Apollo.QueryResult<MarketQuery, MarketQueryVariables>;
|
|
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||||
compileGridData,
|
|
||||||
DealTicketContainer,
|
|
||||||
TradingModeTooltip,
|
|
||||||
} from '@vegaprotocol/deal-ticket';
|
|
||||||
import { MarketInfoContainer } from '@vegaprotocol/market-info';
|
import { MarketInfoContainer } from '@vegaprotocol/market-info';
|
||||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||||
@ -12,9 +8,8 @@ import { TradesContainer } from '@vegaprotocol/trades';
|
|||||||
import { LayoutPriority } from 'allotment';
|
import { LayoutPriority } from 'allotment';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { Market_market } from './__generated__/Market';
|
|
||||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||||
import {
|
import {
|
||||||
@ -23,23 +18,11 @@ import {
|
|||||||
ResizableGrid,
|
ResizableGrid,
|
||||||
ResizableGridPanel,
|
ResizableGridPanel,
|
||||||
ButtonLink,
|
ButtonLink,
|
||||||
PriceCellChange,
|
|
||||||
Link,
|
Link,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import {
|
import { getDateFormat, t } from '@vegaprotocol/react-helpers';
|
||||||
addDecimalsFormatNumber,
|
|
||||||
getDateFormat,
|
|
||||||
t,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import type { CandleClose } from '@vegaprotocol/types';
|
|
||||||
import {
|
|
||||||
AuctionTrigger,
|
|
||||||
AuctionTriggerMapping,
|
|
||||||
MarketTradingMode,
|
|
||||||
MarketTradingModeMapping,
|
|
||||||
} from '@vegaprotocol/types';
|
|
||||||
import { Header, HeaderStat } from '../../components/header';
|
import { Header, HeaderStat } from '../../components/header';
|
||||||
import { AccountsContainer } from '../portfolio/accounts-container';
|
import { AccountsContainer } from '../portfolio/accounts-container';
|
||||||
import {
|
import {
|
||||||
@ -47,6 +30,11 @@ import {
|
|||||||
SelectMarketPopover,
|
SelectMarketPopover,
|
||||||
} from '../../components/select-market';
|
} from '../../components/select-market';
|
||||||
import type { OnCellClickHandler } from '../../components/select-market';
|
import type { OnCellClickHandler } from '../../components/select-market';
|
||||||
|
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||||
|
import { Last24hPriceChange } from '../../components/last-24h-price-change';
|
||||||
|
import { MarketMarkPrice } from '../../components/market-mark-price';
|
||||||
|
import { MarketVolume } from '../../components/market-volume';
|
||||||
|
import { MarketTradingModeComponent } from '../../components/market-trading-mode';
|
||||||
|
|
||||||
const TradingViews = {
|
const TradingViews = {
|
||||||
Candles: CandlesChartContainer,
|
Candles: CandlesChartContainer,
|
||||||
@ -64,7 +52,7 @@ const TradingViews = {
|
|||||||
type TradingView = keyof typeof TradingViews;
|
type TradingView = keyof typeof TradingViews;
|
||||||
|
|
||||||
type ExpiryLabelProps = {
|
type ExpiryLabelProps = {
|
||||||
market: Market_market;
|
market: SingleMarketFieldsFragment;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
|
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
|
||||||
@ -72,7 +60,7 @@ const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
|
|||||||
if (market.marketTimestamps.close === null) {
|
if (market.marketTimestamps.close === null) {
|
||||||
content = t('Not time-based');
|
content = t('Not time-based');
|
||||||
} else {
|
} else {
|
||||||
const closeDate = new Date(market.marketTimestamps.close);
|
const closeDate = new Date(market.marketTimestamps.close as string);
|
||||||
const isExpired = Date.now() - closeDate.valueOf() > 0;
|
const isExpired = Date.now() - closeDate.valueOf() > 0;
|
||||||
const expiryDate = getDateFormat().format(closeDate);
|
const expiryDate = getDateFormat().format(closeDate);
|
||||||
content = `${isExpired ? `${t('Expired')} ` : ''} ${expiryDate}`;
|
content = `${isExpired ? `${t('Expired')} ` : ''} ${expiryDate}`;
|
||||||
@ -81,7 +69,7 @@ const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type ExpiryTooltipContentProps = {
|
type ExpiryTooltipContentProps = {
|
||||||
market: Market_market;
|
market: SingleMarketFieldsFragment;
|
||||||
explorerUrl?: string;
|
explorerUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,7 +102,7 @@ const ExpiryTooltipContent = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface TradeMarketHeaderProps {
|
interface TradeMarketHeaderProps {
|
||||||
market: Market_market;
|
market: SingleMarketFieldsFragment;
|
||||||
onSelect: (marketId: string) => void;
|
onSelect: (marketId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,9 +113,6 @@ export const TradeMarketHeader = ({
|
|||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||||
|
|
||||||
const candlesClose: string[] = (market?.candlesConnection?.edges || [])
|
|
||||||
.map((candle) => candle?.node.close)
|
|
||||||
.filter((c): c is CandleClose => c !== null);
|
|
||||||
const symbol =
|
const symbol =
|
||||||
market.tradableInstrument.instrument.product?.settlementAsset?.symbol;
|
market.tradableInstrument.instrument.product?.settlementAsset?.symbol;
|
||||||
|
|
||||||
@ -158,51 +143,10 @@ export const TradeMarketHeader = ({
|
|||||||
>
|
>
|
||||||
<ExpiryLabel market={market} />
|
<ExpiryLabel market={market} />
|
||||||
</HeaderStat>
|
</HeaderStat>
|
||||||
<HeaderStat heading={t('Price')}>
|
<MarketMarkPrice marketId={market.id} />
|
||||||
<div data-testid="mark-price">
|
<Last24hPriceChange marketId={market.id} />
|
||||||
{market.data && market.data.markPrice !== '0'
|
<MarketVolume marketId={market.id} />
|
||||||
? addDecimalsFormatNumber(
|
<MarketTradingModeComponent marketId={market.id} onSelect={onSelect} />
|
||||||
market.data.markPrice,
|
|
||||||
market.decimalPlaces
|
|
||||||
)
|
|
||||||
: '-'}
|
|
||||||
</div>
|
|
||||||
</HeaderStat>
|
|
||||||
<HeaderStat heading={t('Change (24h)')}>
|
|
||||||
<PriceCellChange
|
|
||||||
candles={candlesClose}
|
|
||||||
decimalPlaces={market.decimalPlaces}
|
|
||||||
/>
|
|
||||||
</HeaderStat>
|
|
||||||
<HeaderStat heading={t('Volume')}>
|
|
||||||
<div data-testid="trading-volume">
|
|
||||||
{market.data && market.data.indicativeVolume !== '0'
|
|
||||||
? addDecimalsFormatNumber(
|
|
||||||
market.data.indicativeVolume,
|
|
||||||
market.positionDecimalPlaces
|
|
||||||
)
|
|
||||||
: '-'}
|
|
||||||
</div>
|
|
||||||
</HeaderStat>
|
|
||||||
<HeaderStat
|
|
||||||
heading={t('Trading mode')}
|
|
||||||
description={
|
|
||||||
<TradingModeTooltip
|
|
||||||
market={market}
|
|
||||||
compiledGrid={compileGridData(market, onSelect)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div data-testid="trading-mode">
|
|
||||||
{market.tradingMode ===
|
|
||||||
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
|
||||||
market.data?.trigger &&
|
|
||||||
market.data.trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
|
||||||
? `${MarketTradingModeMapping[market.tradingMode]}
|
|
||||||
- ${AuctionTriggerMapping[market.data.trigger]}`
|
|
||||||
: MarketTradingModeMapping[market.tradingMode]}
|
|
||||||
</div>
|
|
||||||
</HeaderStat>
|
|
||||||
{symbol ? (
|
{symbol ? (
|
||||||
<HeaderStat heading={t('Settlement asset')}>
|
<HeaderStat heading={t('Settlement asset')}>
|
||||||
<div data-testid="trading-mode">
|
<div data-testid="trading-mode">
|
||||||
@ -221,15 +165,18 @@ export const TradeMarketHeader = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface TradeGridProps {
|
interface TradeGridProps {
|
||||||
market: Market_market;
|
market: SingleMarketFieldsFragment;
|
||||||
onSelect: (marketId: string) => void;
|
onSelect: (marketId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
const MainGrid = ({
|
||||||
return (
|
marketId,
|
||||||
<div className="h-full grid grid-rows-[min-content_1fr]">
|
onSelect,
|
||||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
}: {
|
||||||
<ResizableGrid vertical={true}>
|
marketId: string;
|
||||||
|
onSelect: (marketId: string) => void;
|
||||||
|
}) => (
|
||||||
|
<ResizableGrid vertical>
|
||||||
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
||||||
<ResizableGrid proportionalLayout={false} minSize={200}>
|
<ResizableGrid proportionalLayout={false} minSize={200}>
|
||||||
<ResizableGridPanel
|
<ResizableGridPanel
|
||||||
@ -240,10 +187,10 @@ export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
|||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab id="candles" name={t('Candles')}>
|
<Tab id="candles" name={t('Candles')}>
|
||||||
<TradingViews.Candles marketId={market.id} />
|
<TradingViews.Candles marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="depth" name={t('Depth')}>
|
<Tab id="depth" name={t('Depth')}>
|
||||||
<TradingViews.Depth marketId={market.id} />
|
<TradingViews.Depth marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
@ -256,11 +203,11 @@ export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
|||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab id="ticket" name={t('Ticket')}>
|
<Tab id="ticket" name={t('Ticket')}>
|
||||||
<TradingViews.Ticket marketId={market.id} />
|
<TradingViews.Ticket marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="info" name={t('Info')}>
|
<Tab id="info" name={t('Info')}>
|
||||||
<TradingViews.Info
|
<TradingViews.Info
|
||||||
marketId={market.id}
|
marketId={marketId}
|
||||||
onSelect={(id: string) => {
|
onSelect={(id: string) => {
|
||||||
onSelect(id);
|
onSelect(id);
|
||||||
}}
|
}}
|
||||||
@ -277,10 +224,10 @@ export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
|||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab id="orderbook" name={t('Orderbook')}>
|
<Tab id="orderbook" name={t('Orderbook')}>
|
||||||
<TradingViews.Orderbook marketId={market.id} />
|
<TradingViews.Orderbook marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="trades" name={t('Trades')}>
|
<Tab id="trades" name={t('Trades')}>
|
||||||
<TradingViews.Trades marketId={market.id} />
|
<TradingViews.Trades marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
@ -310,6 +257,14 @@ export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
|||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
</ResizableGridPanel>
|
</ResizableGridPanel>
|
||||||
</ResizableGrid>
|
</ResizableGrid>
|
||||||
|
);
|
||||||
|
const MainGridWrapped = memo(MainGrid);
|
||||||
|
|
||||||
|
export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
|
||||||
|
return (
|
||||||
|
<div className="h-full grid grid-rows-[min-content_1fr]">
|
||||||
|
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||||
|
<MainGridWrapped marketId={market.id} onSelect={onSelect} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -329,7 +284,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface TradePanelsProps {
|
interface TradePanelsProps {
|
||||||
market: Market_market;
|
market: SingleMarketFieldsFragment;
|
||||||
onSelect: (marketId: string) => void;
|
onSelect: (marketId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,20 +292,16 @@ export const TradePanels = ({ market, onSelect }: TradePanelsProps) => {
|
|||||||
const [view, setView] = useState<TradingView>('Candles');
|
const [view, setView] = useState<TradingView>('Candles');
|
||||||
|
|
||||||
const renderView = () => {
|
const renderView = () => {
|
||||||
const Component = TradingViews[view];
|
const Component = memo<{
|
||||||
|
marketId: string;
|
||||||
|
onSelect: (marketId: string) => void;
|
||||||
|
}>(TradingViews[view]);
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
throw new Error(`No component for view: ${view}`);
|
throw new Error(`No component for view: ${view}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <Component marketId={market.id} onSelect={onSelect} />;
|
||||||
<Component
|
|
||||||
marketId={market.id}
|
|
||||||
onSelect={(id: string) => {
|
|
||||||
onSelect(id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -9,6 +9,8 @@ interface GlobalStore {
|
|||||||
marketId: string | null;
|
marketId: string | null;
|
||||||
pageTitle: string | null;
|
pageTitle: string | null;
|
||||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||||
|
updateTitle: (title: string) => void;
|
||||||
|
updateMarketId: (marketId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGlobalStore = create<GlobalStore>((set) => ({
|
export const useGlobalStore = create<GlobalStore>((set) => ({
|
||||||
@ -24,4 +26,9 @@ export const useGlobalStore = create<GlobalStore>((set) => ({
|
|||||||
LocalStorage.setItem('marketId', state.marketId);
|
LocalStorage.setItem('marketId', state.marketId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateTitle: (title: string) => set({ pageTitle: title }),
|
||||||
|
updateMarketId: (marketId: string) => {
|
||||||
|
set({ marketId });
|
||||||
|
LocalStorage.setItem('marketId', marketId);
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -11,7 +11,7 @@ import type { MarketDataGridProps } from './market-data-grid';
|
|||||||
import type { DealTicketMarketFragment } from '../deal-ticket/__generated___/DealTicket';
|
import type { DealTicketMarketFragment } from '../deal-ticket/__generated___/DealTicket';
|
||||||
|
|
||||||
export const compileGridData = (
|
export const compileGridData = (
|
||||||
market: DealTicketMarketFragment,
|
market: Omit<DealTicketMarketFragment, 'depth'>,
|
||||||
onSelect?: (id: string) => void
|
onSelect?: (id: string) => void
|
||||||
): { label: ReactNode; value?: ReactNode }[] => {
|
): { label: ReactNode; value?: ReactNode }[] => {
|
||||||
const grid: MarketDataGridProps['grid'] = [];
|
const grid: MarketDataGridProps['grid'] = [];
|
||||||
|
@ -5,18 +5,17 @@ import type { ReactNode } from 'react';
|
|||||||
import { MarketDataGrid } from './market-data-grid';
|
import { MarketDataGrid } from './market-data-grid';
|
||||||
|
|
||||||
type TradingModeTooltipProps = {
|
type TradingModeTooltipProps = {
|
||||||
market: {
|
tradingMode: MarketTradingMode | null;
|
||||||
tradingMode: MarketTradingMode;
|
trigger: AuctionTrigger | null;
|
||||||
data: { trigger: AuctionTrigger | null } | null;
|
|
||||||
};
|
|
||||||
compiledGrid: { label: ReactNode; value?: ReactNode }[];
|
compiledGrid: { label: ReactNode; value?: ReactNode }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TradingModeTooltip = ({
|
export const TradingModeTooltip = ({
|
||||||
market,
|
tradingMode,
|
||||||
|
trigger,
|
||||||
compiledGrid,
|
compiledGrid,
|
||||||
}: TradingModeTooltipProps) => {
|
}: TradingModeTooltipProps) => {
|
||||||
switch (market.tradingMode) {
|
switch (tradingMode) {
|
||||||
case MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
case MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -44,7 +43,7 @@ export const TradingModeTooltip = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
case MarketTradingMode.TRADING_MODE_MONITORING_AUCTION: {
|
case MarketTradingMode.TRADING_MODE_MONITORING_AUCTION: {
|
||||||
switch (market.data?.trigger) {
|
switch (trigger) {
|
||||||
case AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY: {
|
case AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY: {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -39,7 +39,7 @@ export const useFillsList = ({ partyId, gridRef, scrolledToTop }: Props) => {
|
|||||||
delta,
|
delta,
|
||||||
}: {
|
}: {
|
||||||
data: (TradeEdge | null)[] | null;
|
data: (TradeEdge | null)[] | null;
|
||||||
delta: Trade[];
|
delta?: Trade[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!gridRef.current?.api) {
|
if (!gridRef.current?.api) {
|
||||||
return false;
|
return false;
|
||||||
@ -48,7 +48,7 @@ export const useFillsList = ({ partyId, gridRef, scrolledToTop }: Props) => {
|
|||||||
if (!scrolledToTop.current) {
|
if (!scrolledToTop.current) {
|
||||||
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
||||||
if (createdAt) {
|
if (createdAt) {
|
||||||
newRows.current += delta.filter(
|
newRows.current += (delta || []).filter(
|
||||||
(trade) => trade.createdAt > createdAt
|
(trade) => trade.createdAt > createdAt
|
||||||
).length;
|
).length;
|
||||||
}
|
}
|
||||||
|
@ -93,12 +93,12 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
|
|||||||
({
|
({
|
||||||
delta: deltas,
|
delta: deltas,
|
||||||
}: {
|
}: {
|
||||||
delta: MarketDepthSubscription_marketsDepthUpdate[];
|
delta?: MarketDepthSubscription_marketsDepthUpdate[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!dataRef.current) {
|
if (!dataRef.current) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const delta of deltas) {
|
for (const delta of deltas || []) {
|
||||||
if (delta.marketId !== marketId) {
|
if (delta.marketId !== marketId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -63,12 +63,12 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
({
|
({
|
||||||
delta: deltas,
|
delta: deltas,
|
||||||
}: {
|
}: {
|
||||||
delta: MarketDepthSubscription_marketsDepthUpdate[];
|
delta?: MarketDepthSubscription_marketsDepthUpdate[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!dataRef.current.rows) {
|
if (!dataRef.current.rows) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const delta of deltas) {
|
for (const delta of deltas || []) {
|
||||||
if (delta.marketId !== marketId) {
|
if (delta.marketId !== marketId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
93
libs/market-list/src/lib/__generated___/market.ts
Normal file
93
libs/market-list/src/lib/__generated___/market.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { Schema as Types } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type SingleMarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, oracleSpecForTradingTermination: { __typename?: 'OracleSpec', id: string }, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open?: string | null, close?: string | null } };
|
||||||
|
|
||||||
|
export type MarketQueryVariables = Types.Exact<{
|
||||||
|
marketId: Types.Scalars['ID'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type MarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, oracleSpecForTradingTermination: { __typename?: 'OracleSpec', id: string }, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open?: string | null, close?: string | null } } | null };
|
||||||
|
|
||||||
|
export const SingleMarketFieldsFragmentDoc = gql`
|
||||||
|
fragment SingleMarketFields on Market {
|
||||||
|
id
|
||||||
|
decimalPlaces
|
||||||
|
positionDecimalPlaces
|
||||||
|
state
|
||||||
|
tradingMode
|
||||||
|
fees {
|
||||||
|
factors {
|
||||||
|
makerFee
|
||||||
|
infrastructureFee
|
||||||
|
liquidityFee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tradableInstrument {
|
||||||
|
instrument {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
code
|
||||||
|
metadata {
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
... on Future {
|
||||||
|
oracleSpecForTradingTermination {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
settlementAsset {
|
||||||
|
id
|
||||||
|
symbol
|
||||||
|
name
|
||||||
|
decimals
|
||||||
|
}
|
||||||
|
quoteName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
marketTimestamps {
|
||||||
|
open
|
||||||
|
close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const MarketDocument = gql`
|
||||||
|
query Market($marketId: ID!) {
|
||||||
|
market(id: $marketId) {
|
||||||
|
...SingleMarketFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${SingleMarketFieldsFragmentDoc}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useMarketQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useMarketQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useMarketQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useMarketQuery({
|
||||||
|
* variables: {
|
||||||
|
* marketId: // value for 'marketId'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useMarketQuery(baseOptions: Apollo.QueryHookOptions<MarketQuery, MarketQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<MarketQuery, MarketQueryVariables>(MarketDocument, options);
|
||||||
|
}
|
||||||
|
export function useMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketQuery, MarketQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<MarketQuery, MarketQueryVariables>(MarketDocument, options);
|
||||||
|
}
|
||||||
|
export type MarketQueryHookResult = ReturnType<typeof useMarketQuery>;
|
||||||
|
export type MarketLazyQueryHookResult = ReturnType<typeof useMarketLazyQuery>;
|
||||||
|
export type MarketQueryResult = Apollo.QueryResult<MarketQuery, MarketQueryVariables>;
|
@ -8,6 +8,7 @@ export * from './markets-data-provider';
|
|||||||
export * from './markets-provider';
|
export * from './markets-provider';
|
||||||
export * from './__generated___/market-candles';
|
export * from './__generated___/market-candles';
|
||||||
export * from './__generated___/market-data';
|
export * from './__generated___/market-data';
|
||||||
|
export * from './__generated___/market';
|
||||||
export * from './__generated___/markets';
|
export * from './__generated___/markets';
|
||||||
export * from './__generated___/markets-candles';
|
export * from './__generated___/markets-candles';
|
||||||
export * from './__generated___/markets-data';
|
export * from './__generated___/markets-data';
|
||||||
|
@ -11,9 +11,8 @@ import {
|
|||||||
|
|
||||||
export type Candle = MarketCandlesFieldsFragment;
|
export type Candle = MarketCandlesFieldsFragment;
|
||||||
|
|
||||||
const update = (data: Candle[], delta: Candle) => {
|
const update = (data: Candle[], delta: Candle) =>
|
||||||
return data && delta ? [...data, delta] : data;
|
data && delta ? [...data, delta] : data;
|
||||||
};
|
|
||||||
|
|
||||||
const getData = (responseData: MarketCandlesQuery): Candle[] | null =>
|
const getData = (responseData: MarketCandlesQuery): Candle[] | null =>
|
||||||
responseData?.marketsConnection?.edges[0]?.node.candlesConnection?.edges
|
responseData?.marketsConnection?.edges[0]?.node.candlesConnection?.edges
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import { makeDerivedDataProvider } from '@vegaprotocol/react-helpers';
|
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
import { MarketDocument } from './__generated___/market';
|
||||||
|
import type {
|
||||||
|
MarketQuery,
|
||||||
|
SingleMarketFieldsFragment,
|
||||||
|
} from './__generated___/market';
|
||||||
|
|
||||||
import type { Market } from './markets-provider';
|
const getData = (
|
||||||
import { marketsProvider } from './markets-provider';
|
responseData: MarketQuery
|
||||||
|
): SingleMarketFieldsFragment | null => responseData?.market || null;
|
||||||
|
|
||||||
export const marketProvider = makeDerivedDataProvider<Market, never>(
|
export const marketProvider = makeDataProvider<
|
||||||
[(callback, client) => marketsProvider(callback, client)], // omit variables param
|
MarketQuery,
|
||||||
([markets], variables) => {
|
SingleMarketFieldsFragment,
|
||||||
if (markets) {
|
never,
|
||||||
const market = (markets as Market[]).find(
|
never
|
||||||
(market) => market.id === variables?.['marketId']
|
>({
|
||||||
);
|
query: MarketDocument,
|
||||||
if (market) {
|
getData,
|
||||||
return market;
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
48
libs/market-list/src/lib/market.graphql
Normal file
48
libs/market-list/src/lib/market.graphql
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
fragment SingleMarketFields on Market {
|
||||||
|
id
|
||||||
|
decimalPlaces
|
||||||
|
positionDecimalPlaces
|
||||||
|
state
|
||||||
|
tradingMode
|
||||||
|
fees {
|
||||||
|
factors {
|
||||||
|
makerFee
|
||||||
|
infrastructureFee
|
||||||
|
liquidityFee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tradableInstrument {
|
||||||
|
instrument {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
code
|
||||||
|
metadata {
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
... on Future {
|
||||||
|
oracleSpecForTradingTermination {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
settlementAsset {
|
||||||
|
id
|
||||||
|
symbol
|
||||||
|
name
|
||||||
|
decimals
|
||||||
|
}
|
||||||
|
quoteName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
marketTimestamps {
|
||||||
|
open
|
||||||
|
close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query Market($marketId: ID!) {
|
||||||
|
market(id: $marketId) {
|
||||||
|
...SingleMarketFields
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ export const useOrderListData = ({
|
|||||||
}, [gridRef]);
|
}, [gridRef]);
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
({ data, delta }: { data: (OrderEdge | null)[]; delta: Order[] }) => {
|
({ data, delta }: { data: (OrderEdge | null)[]; delta?: Order[] }) => {
|
||||||
if (!gridRef.current?.api) {
|
if (!gridRef.current?.api) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ export const useOrderListData = ({
|
|||||||
if (!scrolledToTop.current) {
|
if (!scrolledToTop.current) {
|
||||||
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
||||||
if (createdAt) {
|
if (createdAt) {
|
||||||
newRows.current += delta.filter(
|
newRows.current += (delta || []).filter(
|
||||||
(trade) => trade.createdAt > createdAt
|
(trade) => trade.createdAt > createdAt
|
||||||
).length;
|
).length;
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,12 @@ export function useDataProvider<Data, Delta>({
|
|||||||
update,
|
update,
|
||||||
insert,
|
insert,
|
||||||
variables,
|
variables,
|
||||||
|
updateOnInit,
|
||||||
noUpdate,
|
noUpdate,
|
||||||
skip,
|
skip,
|
||||||
}: {
|
}: {
|
||||||
dataProvider: Subscribe<Data, Delta>;
|
dataProvider: Subscribe<Data, Delta>;
|
||||||
update?: ({ delta, data }: { delta: Delta; data: Data }) => boolean;
|
update?: ({ delta, data }: { delta?: Delta; data: Data }) => boolean;
|
||||||
insert?: ({
|
insert?: ({
|
||||||
insertionData,
|
insertionData,
|
||||||
data,
|
data,
|
||||||
@ -41,6 +42,7 @@ export function useDataProvider<Data, Delta>({
|
|||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
}) => boolean;
|
}) => boolean;
|
||||||
variables?: OperationVariables;
|
variables?: OperationVariables;
|
||||||
|
updateOnInit?: boolean;
|
||||||
noUpdate?: boolean;
|
noUpdate?: boolean;
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
}) {
|
}) {
|
||||||
@ -103,12 +105,15 @@ export function useDataProvider<Data, Delta>({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initialized.current = true;
|
|
||||||
setTotalCount(totalCount);
|
setTotalCount(totalCount);
|
||||||
setData(data);
|
setData(data);
|
||||||
|
if (updateOnInit && !initialized.current && update && data) {
|
||||||
|
update({ data });
|
||||||
|
}
|
||||||
|
initialized.current = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[update, insert, noUpdate]
|
[update, insert, noUpdate, updateOnInit]
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (skip) {
|
if (skip) {
|
||||||
@ -122,6 +127,7 @@ export function useDataProvider<Data, Delta>({
|
|||||||
flushRef.current = flush;
|
flushRef.current = flush;
|
||||||
reloadRef.current = reload;
|
reloadRef.current = reload;
|
||||||
loadRef.current = load;
|
loadRef.current = load;
|
||||||
|
initialized.current = false;
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [client, initialized, dataProvider, callback, variables, skip]);
|
}, [client, initialized, dataProvider, callback, variables, skip]);
|
||||||
return { data, loading, error, flush, reload, load, totalCount };
|
return { data, loading, error, flush, reload, load, totalCount };
|
||||||
|
@ -47,7 +47,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
|||||||
delta,
|
delta,
|
||||||
}: {
|
}: {
|
||||||
data: (TradeEdge | null)[] | null;
|
data: (TradeEdge | null)[] | null;
|
||||||
delta: Trade[];
|
delta?: Trade[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!gridRef.current?.api) {
|
if (!gridRef.current?.api) {
|
||||||
return false;
|
return false;
|
||||||
@ -56,7 +56,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
|||||||
if (!scrolledToTop.current) {
|
if (!scrolledToTop.current) {
|
||||||
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
||||||
if (createdAt) {
|
if (createdAt) {
|
||||||
newRows.current += delta.filter(
|
newRows.current += (delta || []).filter(
|
||||||
(trade) => trade.createdAt > createdAt
|
(trade) => trade.createdAt > createdAt
|
||||||
).length;
|
).length;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user