Co-authored-by: asiaznik <artur@vegaprotocol.io>
This commit is contained in:
parent
dad953b45b
commit
7ea7edc1e2
@ -1,5 +1,5 @@
|
|||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { UserTrancheBalance } from '../../contexts/app-state/app-state-context';
|
import type { UserTrancheBalance } from '../../contexts/app-state/app-state-context';
|
||||||
|
|
||||||
export interface AssociationBreakdown {
|
export interface AssociationBreakdown {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type ethers from 'ethers';
|
import type ethers from 'ethers';
|
||||||
import type { GetState, SetState } from 'zustand';
|
import type { GetState, SetState } from 'zustand';
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
export interface TxData {
|
export interface TxData {
|
||||||
tx: ethers.ContractTransaction;
|
tx: ethers.ContractTransaction;
|
||||||
|
@ -43,8 +43,8 @@ export const HeaderStat = ({
|
|||||||
testId?: string;
|
testId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const itemClass =
|
const itemClass =
|
||||||
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-default';
|
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-default text-neutral-500 dark:text-neutral-400';
|
||||||
const itemHeading = 'text-neutral-500 dark:text-neutral-400';
|
const itemHeading = 'text-black dark:text-white';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid={testId} className={itemClass}>
|
<div data-testid={testId} className={itemClass}>
|
||||||
|
@ -95,7 +95,7 @@ const Details = ({
|
|||||||
title?: string;
|
title?: string;
|
||||||
}) => (
|
}) => (
|
||||||
<div className="pt-[5px]" data-testid="vega-tx-details" title={title}>
|
<div className="pt-[5px]" data-testid="vega-tx-details" title={title}>
|
||||||
<div className="font-mono text-xs p-2 bg-neutral-100 rounded">
|
<div className="font-mono text-xs p-2 bg-neutral-100 rounded dark:bg-neutral-700 dark:text-white">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
|
|
||||||
interface GlobalStore {
|
interface GlobalStore {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { useAssetsDataProvider } from './assets-data-provider';
|
import { useAssetsDataProvider } from './assets-data-provider';
|
||||||
import { Button, Dialog, Icon, Splash } from '@vegaprotocol/ui-toolkit';
|
import { Button, Dialog, Icon, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { AssetDetailsTable } from './asset-details-table';
|
import { AssetDetailsTable } from './asset-details-table';
|
||||||
import { AssetProposalNotification } from '@vegaprotocol/governance';
|
import { AssetProposalNotification } from '@vegaprotocol/governance';
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
|
|||||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useOrderMarginValidation } from '../../hooks/use-order-margin-validation';
|
import { useOrderMarginValidation } from '../../hooks/use-order-margin-validation';
|
||||||
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
|
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
|
||||||
import { usePersistedOrderStore } from '../../hooks/use-persisted-order';
|
|
||||||
import {
|
import {
|
||||||
getDefaultOrder,
|
getDefaultOrder,
|
||||||
validateMarketState,
|
validateMarketState,
|
||||||
@ -27,6 +26,10 @@ import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
|
|||||||
import { SummaryValidationType } from '../../constants';
|
import { SummaryValidationType } from '../../constants';
|
||||||
import { useHasNoBalance } from '../../hooks/use-has-no-balance';
|
import { useHasNoBalance } from '../../hooks/use-has-no-balance';
|
||||||
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
||||||
|
import {
|
||||||
|
usePersistedOrderStore,
|
||||||
|
usePersistedOrderStoreSubscription,
|
||||||
|
} from '@vegaprotocol/orders';
|
||||||
|
|
||||||
export type TransactionStatus = 'default' | 'pending';
|
export type TransactionStatus = 'default' | 'pending';
|
||||||
|
|
||||||
@ -43,13 +46,13 @@ export type DealTicketFormFields = OrderSubmissionBody['orderSubmission'] & {
|
|||||||
|
|
||||||
export const DealTicket = ({ market, submit }: DealTicketProps) => {
|
export const DealTicket = ({ market, submit }: DealTicketProps) => {
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
// const [persistedOrder, setPersistedOrder] = usePersistedOrder(market);
|
|
||||||
const { getPersistedOrder, setPersistedOrder } = usePersistedOrderStore(
|
const { getPersistedOrder, setPersistedOrder } = usePersistedOrderStore(
|
||||||
(store) => ({
|
(store) => ({
|
||||||
getPersistedOrder: store.getOrder,
|
getPersistedOrder: store.getOrder,
|
||||||
setPersistedOrder: store.setOrder,
|
setPersistedOrder: store.setOrder,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
control,
|
control,
|
||||||
@ -58,11 +61,23 @@ export const DealTicket = ({ market, submit }: DealTicketProps) => {
|
|||||||
setError,
|
setError,
|
||||||
clearErrors,
|
clearErrors,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
} = useForm<DealTicketFormFields>({
|
} = useForm<DealTicketFormFields>({
|
||||||
defaultValues: getPersistedOrder(market.id) || getDefaultOrder(market),
|
defaultValues: getPersistedOrder(market.id) || getDefaultOrder(market),
|
||||||
});
|
});
|
||||||
|
|
||||||
const order = watch();
|
const order = watch();
|
||||||
|
|
||||||
|
watch((orderData) => {
|
||||||
|
setPersistedOrder(orderData as DealTicketFormFields);
|
||||||
|
});
|
||||||
|
|
||||||
|
usePersistedOrderStoreSubscription(market.id, (storedOrder) => {
|
||||||
|
if (order.price !== storedOrder.price) {
|
||||||
|
setValue('price', storedOrder.price);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const marketStateError = validateMarketState(market.data.marketState);
|
const marketStateError = validateMarketState(market.data.marketState);
|
||||||
const hasNoBalance = useHasNoBalance(
|
const hasNoBalance = useHasNoBalance(
|
||||||
market.tradableInstrument.instrument.product.settlementAsset.id
|
market.tradableInstrument.instrument.product.settlementAsset.id
|
||||||
@ -90,9 +105,6 @@ export const DealTicket = ({ market, submit }: DealTicketProps) => {
|
|||||||
errors.summary?.type,
|
errors.summary?.type,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// When order state changes persist it in local storage
|
|
||||||
useEffect(() => setPersistedOrder(order), [order, setPersistedOrder]);
|
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(order: OrderSubmissionBody['orderSubmission']) => {
|
(order: OrderSubmissionBody['orderSubmission']) => {
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
|
||||||
import produce from 'immer';
|
|
||||||
import create from 'zustand';
|
|
||||||
import { persist } from 'zustand/middleware';
|
|
||||||
|
|
||||||
type OrderData = OrderSubmissionBody['orderSubmission'] | null;
|
|
||||||
|
|
||||||
type PersistedOrderStore = {
|
|
||||||
orders: OrderData[];
|
|
||||||
getOrder: (marketId: string) => OrderData | undefined;
|
|
||||||
setOrder: (order: OrderData) => void;
|
|
||||||
clear: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePersistedOrderStore = create(
|
|
||||||
persist<PersistedOrderStore>(
|
|
||||||
(set, get) => ({
|
|
||||||
orders: [],
|
|
||||||
getOrder: (marketId) => {
|
|
||||||
const persisted = get().orders.find((o) => o?.marketId === marketId);
|
|
||||||
return persisted;
|
|
||||||
},
|
|
||||||
setOrder: (order) => {
|
|
||||||
set(
|
|
||||||
produce((store: PersistedOrderStore) => {
|
|
||||||
const persisted = store.orders.find(
|
|
||||||
(o) => o?.marketId === order?.marketId
|
|
||||||
);
|
|
||||||
if (persisted) {
|
|
||||||
Object.assign(persisted, order);
|
|
||||||
} else {
|
|
||||||
store.orders.push(order);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
clear: () => set({ orders: [] }),
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: 'VEGA_DEAL_TICKET_ORDER_STORE',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Orderbook } from './orderbook';
|
import { Orderbook } from './orderbook';
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { addDecimal, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import { marketDepthProvider } from './market-depth-provider';
|
import { marketDepthProvider } from './market-depth-provider';
|
||||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||||
import type { MarketData } from '@vegaprotocol/market-list';
|
import type { MarketData } from '@vegaprotocol/market-list';
|
||||||
@ -16,6 +16,7 @@ import {
|
|||||||
mapMarketData,
|
mapMarketData,
|
||||||
} from './orderbook-data';
|
} from './orderbook-data';
|
||||||
import type { OrderbookData } from './orderbook-data';
|
import type { OrderbookData } from './orderbook-data';
|
||||||
|
import { usePersistedOrderStore } from '@vegaprotocol/orders';
|
||||||
|
|
||||||
interface OrderbookManagerProps {
|
interface OrderbookManagerProps {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
@ -122,7 +123,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
marketDataRef.current = marketData;
|
marketDataRef.current = marketData;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const throttleRunnner = updateOrderbookData.current;
|
const throttleRunner = updateOrderbookData.current;
|
||||||
if (!marketDataRef.current) {
|
if (!marketDataRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -139,7 +140,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
setOrderbookData(dataRef.current);
|
setOrderbookData(dataRef.current);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
throttleRunnner.cancel();
|
throttleRunner.cancel();
|
||||||
};
|
};
|
||||||
}, [data, marketData, resolution]);
|
}, [data, marketData, resolution]);
|
||||||
|
|
||||||
@ -148,6 +149,8 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
flush();
|
flush();
|
||||||
}, [resolution, flush]);
|
}, [resolution, flush]);
|
||||||
|
|
||||||
|
const updatePrice = usePersistedOrderStore((store) => store.updatePrice);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
loading={loading || marketDataLoading || marketLoading}
|
loading={loading || marketDataLoading || marketLoading}
|
||||||
@ -160,6 +163,12 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
positionDecimalPlaces={market?.positionDecimalPlaces ?? 0}
|
positionDecimalPlaces={market?.positionDecimalPlaces ?? 0}
|
||||||
resolution={resolution}
|
resolution={resolution}
|
||||||
onResolutionChange={(resolution: number) => setResolution(resolution)}
|
onResolutionChange={(resolution: number) => setResolution(resolution)}
|
||||||
|
onClick={(price?: string | number) => {
|
||||||
|
if (price) {
|
||||||
|
const priceValue = addDecimal(price, market?.decimalPlaces ?? 0);
|
||||||
|
updatePrice(marketId, priceValue);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
|
@ -21,6 +21,7 @@ interface OrderbookRowProps {
|
|||||||
price: string;
|
price: string;
|
||||||
relativeAsk?: number;
|
relativeAsk?: number;
|
||||||
relativeBid?: number;
|
relativeBid?: number;
|
||||||
|
onClick?: (price?: string | number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderbookRow = React.memo(
|
export const OrderbookRow = React.memo(
|
||||||
@ -37,6 +38,7 @@ export const OrderbookRow = React.memo(
|
|||||||
price,
|
price,
|
||||||
relativeAsk,
|
relativeAsk,
|
||||||
relativeBid,
|
relativeBid,
|
||||||
|
onClick,
|
||||||
}: OrderbookRowProps) => {
|
}: OrderbookRowProps) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -57,6 +59,7 @@ export const OrderbookRow = React.memo(
|
|||||||
<PriceCell
|
<PriceCell
|
||||||
testId={`price-${price}`}
|
testId={`price-${price}`}
|
||||||
value={BigInt(price)}
|
value={BigInt(price)}
|
||||||
|
onClick={() => onClick && onClick(price)}
|
||||||
valueFormatted={addDecimalsFormatNumber(price, decimalPlaces)}
|
valueFormatted={addDecimalsFormatNumber(price, decimalPlaces)}
|
||||||
/>
|
/>
|
||||||
<CumulativeVol
|
<CumulativeVol
|
||||||
|
@ -30,6 +30,7 @@ interface OrderbookProps extends OrderbookData {
|
|||||||
positionDecimalPlaces: number;
|
positionDecimalPlaces: number;
|
||||||
resolution: number;
|
resolution: number;
|
||||||
onResolutionChange: (resolution: number) => void;
|
onResolutionChange: (resolution: number) => void;
|
||||||
|
onClick?: (price?: string | number) => void;
|
||||||
fillGaps?: boolean;
|
fillGaps?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +280,7 @@ export const Orderbook = ({
|
|||||||
resolution,
|
resolution,
|
||||||
fillGaps: initialFillGaps,
|
fillGaps: initialFillGaps,
|
||||||
onResolutionChange,
|
onResolutionChange,
|
||||||
|
onClick,
|
||||||
}: OrderbookProps) => {
|
}: OrderbookProps) => {
|
||||||
const { theme } = useThemeSwitcher();
|
const { theme } = useThemeSwitcher();
|
||||||
const scrollElement = useRef<HTMLDivElement>(null);
|
const scrollElement = useRef<HTMLDivElement>(null);
|
||||||
@ -533,6 +535,7 @@ export const Orderbook = ({
|
|||||||
<OrderbookRow
|
<OrderbookRow
|
||||||
key={data.price}
|
key={data.price}
|
||||||
price={(BigInt(data.price) / BigInt(resolution)).toString()}
|
price={(BigInt(data.price) / BigInt(resolution)).toString()}
|
||||||
|
onClick={onClick}
|
||||||
decimalPlaces={decimalPlaces - Math.log10(resolution)}
|
decimalPlaces={decimalPlaces - Math.log10(resolution)}
|
||||||
positionDecimalPlaces={positionDecimalPlaces}
|
positionDecimalPlaces={positionDecimalPlaces}
|
||||||
bid={data.bid}
|
bid={data.bid}
|
||||||
|
@ -245,7 +245,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
|||||||
colId="amend"
|
colId="amend"
|
||||||
headerName=""
|
headerName=""
|
||||||
field="status"
|
field="status"
|
||||||
minWidth={150}
|
minWidth={100}
|
||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
cellRenderer={({ data, node }: VegaICellRendererParams<Order>) => {
|
cellRenderer={({ data, node }: VegaICellRendererParams<Order>) => {
|
||||||
return data && isOrderAmendable(data) ? (
|
return data && isOrderAmendable(data) ? (
|
||||||
|
@ -4,3 +4,4 @@ export * from './use-order-cancel';
|
|||||||
export * from './use-order-submit';
|
export * from './use-order-submit';
|
||||||
export * from './use-order-edit';
|
export * from './use-order-edit';
|
||||||
export * from './use-order-event';
|
export * from './use-order-event';
|
||||||
|
export * from './use-persisted-order';
|
||||||
|
78
libs/orders/src/lib/order-hooks/use-persisted-order.ts
Normal file
78
libs/orders/src/lib/order-hooks/use-persisted-order.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||||
|
import produce from 'immer';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist, subscribeWithSelector } from 'zustand/middleware';
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
type OrderData = OrderSubmissionBody['orderSubmission'] | null;
|
||||||
|
|
||||||
|
type PersistedOrderStore = {
|
||||||
|
orders: OrderData[];
|
||||||
|
getOrder: (marketId: string) => OrderData | undefined;
|
||||||
|
setOrder: (order: OrderData) => void;
|
||||||
|
clear: () => void;
|
||||||
|
updatePrice: (marketId: string, price: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePersistedOrderStore = create<PersistedOrderStore>()(
|
||||||
|
persist(
|
||||||
|
subscribeWithSelector((set, get) => ({
|
||||||
|
orders: [],
|
||||||
|
getOrder: (marketId: string) => {
|
||||||
|
const current = get() as PersistedOrderStore;
|
||||||
|
const persisted = current.orders.find((o) => o?.marketId === marketId);
|
||||||
|
return persisted;
|
||||||
|
},
|
||||||
|
setOrder: (order: OrderData) => {
|
||||||
|
set(
|
||||||
|
produce((store: PersistedOrderStore) => {
|
||||||
|
const persisted = store.orders.find(
|
||||||
|
(o) => o?.marketId === order?.marketId
|
||||||
|
);
|
||||||
|
if (persisted) {
|
||||||
|
if (!isEqual(persisted, order)) {
|
||||||
|
Object.assign(persisted, order);
|
||||||
|
} else {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
store.orders.push(order);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
clear: () => set({ orders: [] }),
|
||||||
|
updatePrice: (marketId: string, price: string) =>
|
||||||
|
set(
|
||||||
|
produce((store: PersistedOrderStore) => {
|
||||||
|
const persisted = store.orders.find(
|
||||||
|
(o) => o?.marketId === marketId
|
||||||
|
);
|
||||||
|
if (persisted) {
|
||||||
|
persisted.price = price;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: 'VEGA_DEAL_TICKET_ORDER_STORE',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const usePersistedOrderStoreSubscription = (
|
||||||
|
marketId: string,
|
||||||
|
onOrderChange: (order: NonNullable<OrderData>) => void
|
||||||
|
) => {
|
||||||
|
const selector = (state: PersistedOrderStore) =>
|
||||||
|
state.orders.find((o) => o?.marketId === marketId);
|
||||||
|
const action = (storedOrder: OrderData | undefined) => {
|
||||||
|
if (storedOrder) {
|
||||||
|
onOrderChange(storedOrder);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsubscribe = usePersistedOrderStore.subscribe(selector, action);
|
||||||
|
useEffect(() => () => unsubscribe(), [unsubscribe]);
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { LocalStorage } from '../lib/storage';
|
import { LocalStorage } from '../lib/storage';
|
||||||
|
|
||||||
const THEME_STORAGE_KEY = 'theme';
|
const THEME_STORAGE_KEY = 'theme';
|
||||||
|
@ -4,11 +4,12 @@ export interface IPriceCellProps {
|
|||||||
value: number | bigint | null | undefined;
|
value: number | bigint | null | undefined;
|
||||||
valueFormatted: string;
|
valueFormatted: string;
|
||||||
testId?: string;
|
testId?: string;
|
||||||
|
onClick?: (price?: string | number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PriceCell = memo(
|
export const PriceCell = memo(
|
||||||
forwardRef<HTMLSpanElement, IPriceCellProps>(
|
forwardRef<HTMLSpanElement, IPriceCellProps>(
|
||||||
({ value, valueFormatted, testId }: IPriceCellProps, ref) => {
|
({ value, valueFormatted, testId, onClick }: IPriceCellProps, ref) => {
|
||||||
if (!isNumeric(value)) {
|
if (!isNumeric(value)) {
|
||||||
return (
|
return (
|
||||||
<span data-testid="price" ref={ref}>
|
<span data-testid="price" ref={ref}>
|
||||||
@ -20,7 +21,25 @@ export const PriceCell = memo(
|
|||||||
const valueSplit: string[] = decimalSeparator
|
const valueSplit: string[] = decimalSeparator
|
||||||
? valueFormatted.split(decimalSeparator).map((v) => `${v}`)
|
? valueFormatted.split(decimalSeparator).map((v) => `${v}`)
|
||||||
: [`${value}`];
|
: [`${value}`];
|
||||||
return (
|
return onClick ? (
|
||||||
|
<button
|
||||||
|
onClick={() => onClick(value)}
|
||||||
|
className="hover:dark:bg-neutral-800 hover:bg-neutral-200"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
ref={ref}
|
||||||
|
className="font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir"
|
||||||
|
data-testid={testId || 'price'}
|
||||||
|
title={valueFormatted}
|
||||||
|
>
|
||||||
|
{valueSplit[0]}
|
||||||
|
{valueSplit[1] ? decimalSeparator : null}
|
||||||
|
{valueSplit[1] ? (
|
||||||
|
<span className="opacity-60">{valueSplit[1]}</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
<span
|
<span
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir"
|
className="font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir"
|
||||||
|
@ -10,6 +10,7 @@ import { MAX_TRADES, tradesWithMarketProvider } from './trades-data-provider';
|
|||||||
import { TradesTable } from './trades-table';
|
import { TradesTable } from './trades-table';
|
||||||
import type { Trade, TradeEdge } from './trades-data-provider';
|
import type { Trade, TradeEdge } from './trades-data-provider';
|
||||||
import type { TradesQueryVariables } from './__generated__/Trades';
|
import type { TradesQueryVariables } from './__generated__/Trades';
|
||||||
|
import { usePersistedOrderStore } from '@vegaprotocol/orders';
|
||||||
|
|
||||||
interface TradesContainerProps {
|
interface TradesContainerProps {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
@ -21,6 +22,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
|||||||
const totalCountRef = useRef<number | undefined>(undefined);
|
const totalCountRef = useRef<number | undefined>(undefined);
|
||||||
const newRows = useRef(0);
|
const newRows = useRef(0);
|
||||||
const scrolledToTop = useRef(true);
|
const scrolledToTop = useRef(true);
|
||||||
|
const updatePrice = usePersistedOrderStore((store) => store.updatePrice);
|
||||||
|
|
||||||
const variables = useMemo<TradesQueryVariables>(
|
const variables = useMemo<TradesQueryVariables>(
|
||||||
() => ({ marketId, maxTrades: MAX_TRADES }),
|
() => ({ marketId, maxTrades: MAX_TRADES }),
|
||||||
@ -113,6 +115,11 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
|||||||
datasource={{ getRows }}
|
datasource={{ getRows }}
|
||||||
onBodyScrollEnd={onBodyScrollEnd}
|
onBodyScrollEnd={onBodyScrollEnd}
|
||||||
onBodyScroll={onBodyScroll}
|
onBodyScroll={onBodyScroll}
|
||||||
|
onClick={(price?: string) => {
|
||||||
|
if (price) {
|
||||||
|
updatePrice(marketId, price);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import { AgGridColumn } from 'ag-grid-react';
|
import { AgGridColumn } from 'ag-grid-react';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
import type { VegaICellRendererParams } from '@vegaprotocol/ui-toolkit';
|
||||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||||
import {
|
import {
|
||||||
addDecimal,
|
addDecimal,
|
||||||
@ -49,6 +50,7 @@ export interface Datasource extends IDatasource {
|
|||||||
interface Props extends AgGridReactProps {
|
interface Props extends AgGridReactProps {
|
||||||
rowData?: Trade[] | null;
|
rowData?: Trade[] | null;
|
||||||
datasource?: Datasource;
|
datasource?: Datasource;
|
||||||
|
onClick?: (price?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TradesTableValueFormatterParams = Omit<
|
type TradesTableValueFormatterParams = Omit<
|
||||||
@ -87,6 +89,27 @@ export const TradesTable = forwardRef<AgGridReact, Props>((props, ref) => {
|
|||||||
}
|
}
|
||||||
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
|
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
|
||||||
}}
|
}}
|
||||||
|
cellRenderer={({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: VegaICellRendererParams<Trade, 'price'>) => {
|
||||||
|
if (!data?.market || !value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
props.onClick &&
|
||||||
|
props.onClick(
|
||||||
|
addDecimal(value, data.market?.decimalPlaces || 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="hover:dark:bg-neutral-800 hover:bg-neutral-200"
|
||||||
|
>
|
||||||
|
{addDecimalsFormatNumber(value, data.market.decimalPlaces)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
headerName={t('Size')}
|
headerName={t('Size')}
|
||||||
|
@ -133,7 +133,7 @@ export const Toast = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="flex-1 p-2 pr-6 text-sm overflow-auto"
|
className="flex-1 p-2 pr-6 text-sm overflow-auto dark:bg-black dark:text-white"
|
||||||
data-testid="toast-content"
|
data-testid="toast-content"
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
|
@ -7,7 +7,7 @@ import random from 'lodash/random';
|
|||||||
import sample from 'lodash/sample';
|
import sample from 'lodash/sample';
|
||||||
import uniqueId from 'lodash/uniqueId';
|
import uniqueId from 'lodash/uniqueId';
|
||||||
import { useToasts } from './use-toasts';
|
import { useToasts } from './use-toasts';
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { useEffect } from '@storybook/addons';
|
import { useEffect } from '@storybook/addons';
|
||||||
import { formatNumber } from '@vegaprotocol/react-helpers';
|
import { formatNumber } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { Toast } from './toast';
|
import type { Toast } from './toast';
|
||||||
|
|
||||||
type ToastsStore = {
|
type ToastsStore = {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
const actualCreate = jest.requireActual('zustand').default; // if using jest
|
const zu = jest.requireActual('zustand'); // if using jest
|
||||||
|
|
||||||
// a variable to hold reset functions for all stores declared in the app
|
// a variable to hold reset functions for all stores declared in the app
|
||||||
const storeResetFns = new Set();
|
const storeResetFns = new Set();
|
||||||
|
|
||||||
// when creating a store, we get its initial state, create a reset function and add it in the set
|
// when creating a store, we get its initial state, create a reset function and add it in the set
|
||||||
const create = (createState) => {
|
export const create = (createState) => {
|
||||||
const store = actualCreate(createState);
|
const store = zu.create(createState);
|
||||||
const initialState = store.getState();
|
const initialState = store.getState();
|
||||||
storeResetFns.add(() => store.setState(initialState, true));
|
storeResetFns.add(() => store.setState(initialState, true));
|
||||||
return store;
|
return store;
|
||||||
@ -16,5 +16,3 @@ const create = (createState) => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
act(() => storeResetFns.forEach((resetFn) => resetFn()));
|
act(() => storeResetFns.forEach((resetFn) => resetFn()));
|
||||||
});
|
});
|
||||||
|
|
||||||
export default create;
|
|
||||||
|
@ -23,10 +23,13 @@ import { ChainIdDocument } from '@vegaprotocol/react-helpers';
|
|||||||
|
|
||||||
const mockUpdateDialogOpen = jest.fn();
|
const mockUpdateDialogOpen = jest.fn();
|
||||||
const mockCloseVegaDialog = jest.fn();
|
const mockCloseVegaDialog = jest.fn();
|
||||||
jest.mock('zustand', () => () => () => ({
|
|
||||||
|
jest.mock('zustand', () => ({
|
||||||
|
create: () => () => ({
|
||||||
updateVegaWalletDialog: mockUpdateDialogOpen,
|
updateVegaWalletDialog: mockUpdateDialogOpen,
|
||||||
closeVegaWalletDialog: mockCloseVegaDialog,
|
closeVegaWalletDialog: mockCloseVegaDialog,
|
||||||
vegaWalletDialogOpen: true,
|
vegaWalletDialogOpen: true,
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let defaultProps: VegaConnectDialogProps;
|
let defaultProps: VegaConnectDialogProps;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} from './connectors';
|
} from './connectors';
|
||||||
import { determineId } from './utils';
|
import { determineId } from './utils';
|
||||||
|
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { VegaTxState } from './use-vega-transaction';
|
import type { VegaTxState } from './use-vega-transaction';
|
||||||
import { VegaTxStatus } from './use-vega-transaction';
|
import { VegaTxStatus } from './use-vega-transaction';
|
||||||
import type {
|
import type {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
const actualCreate = jest.requireActual('zustand').default; // if using jest
|
const zu = jest.requireActual('zustand'); // if using jest
|
||||||
|
|
||||||
// a variable to hold reset functions for all stores declared in the app
|
// a variable to hold reset functions for all stores declared in the app
|
||||||
const storeResetFns = new Set();
|
const storeResetFns = new Set();
|
||||||
|
|
||||||
// when creating a store, we get its initial state, create a reset function and add it in the set
|
// when creating a store, we get its initial state, create a reset function and add it in the set
|
||||||
const create = (createState) => {
|
export const create = (createState) => {
|
||||||
const store = actualCreate(createState);
|
const store = zu.create(createState);
|
||||||
const initialState = store.getState();
|
const initialState = store.getState();
|
||||||
storeResetFns.add(() => store.setState(initialState, true));
|
storeResetFns.add(() => store.setState(initialState, true));
|
||||||
return store;
|
return store;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
|
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
|
||||||
import type { CollateralBridge } from '@vegaprotocol/smart-contracts';
|
import type { CollateralBridge } from '@vegaprotocol/smart-contracts';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import type BigNumber from 'bignumber.js';
|
import type BigNumber from 'bignumber.js';
|
||||||
import type { WithdrawalBusEventFieldsFragment } from '@vegaprotocol/wallet';
|
import type { WithdrawalBusEventFieldsFragment } from '@vegaprotocol/wallet';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { Web3ReactHooks } from '@web3-react/core';
|
import type { Web3ReactHooks } from '@web3-react/core';
|
||||||
import type { Connector } from '@web3-react/types';
|
import type { Connector } from '@web3-react/types';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { Asset } from '@vegaprotocol/assets';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
export interface WithdrawStore {
|
export interface WithdrawStore {
|
||||||
asset: Asset | undefined;
|
asset: Asset | undefined;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import create from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"zod": "^3.17.3",
|
"zod": "^3.17.3",
|
||||||
"zustand": "^4.0.0-rc.1"
|
"zustand": "^4.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@apollo/react-testing": "^4.0.0",
|
"@apollo/react-testing": "^4.0.0",
|
||||||
|
@ -23160,13 +23160,20 @@ zod@^3.17.3:
|
|||||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473"
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473"
|
||||||
integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==
|
integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==
|
||||||
|
|
||||||
zustand@^4.0.0-beta.2, zustand@^4.0.0-rc.1:
|
zustand@^4.0.0-beta.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.1.2.tgz#4912b24741662d8a84ed1cb52198471cb369c4b6"
|
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.1.2.tgz#4912b24741662d8a84ed1cb52198471cb369c4b6"
|
||||||
integrity sha512-gcRaKchcxFPbImrBb/BKgujOhHhik9YhVpIeP87ETT7uokEe2Szu7KkuZ9ghjtD+/KKkcrRNktR2AiLXPIbKIQ==
|
integrity sha512-gcRaKchcxFPbImrBb/BKgujOhHhik9YhVpIeP87ETT7uokEe2Szu7KkuZ9ghjtD+/KKkcrRNktR2AiLXPIbKIQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
use-sync-external-store "1.2.0"
|
use-sync-external-store "1.2.0"
|
||||||
|
|
||||||
|
zustand@^4.3.2:
|
||||||
|
version "4.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.2.tgz#bb121fcad84c5a569e94bd1a2695e1a93ba85d39"
|
||||||
|
integrity sha512-rd4haDmlwMTVWVqwvgy00ny8rtti/klRoZjFbL/MAcDnmD5qSw/RZc+Vddstdv90M5Lv6RPgWvm1Hivyn0QgJw==
|
||||||
|
dependencies:
|
||||||
|
use-sync-external-store "1.2.0"
|
||||||
|
|
||||||
zwitch@^1.0.0:
|
zwitch@^1.0.0:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
||||||
|
Loading…
Reference in New Issue
Block a user