Compare commits

..
Author SHA1 Message Date
jaredvu af5d11e058 Change dep in hook to fix input bug 2023-11-12 21:45:13 -08:00
21 changed files with 111 additions and 266 deletions
+1 -1
View File
@@ -39,7 +39,7 @@
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/v4-abacus": "^1.0.26",
"@dydxprotocol/v4-abacus": "^1.0.24",
"@dydxprotocol/v4-client-js": "^1.0.0",
"@dydxprotocol/v4-localization": "^1.0.5",
"@ethersproject/providers": "^5.7.2",
+4 -4
View File
@@ -23,8 +23,8 @@ dependencies:
specifier: ^0.31.0
version: 0.31.0
'@dydxprotocol/v4-abacus':
specifier: ^1.0.26
version: 1.0.26
specifier: ^1.0.24
version: 1.0.24
'@dydxprotocol/v4-client-js':
specifier: ^1.0.0
version: 1.0.0
@@ -984,8 +984,8 @@ packages:
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
dev: true
/@dydxprotocol/v4-abacus@1.0.26:
resolution: {integrity: sha512-WS3NEjkIVy9TM+I5BWKNbTrK3vhwx6AW8ow9fMl4+AiKnoLQmGa7c/4YErv/zFfoidHgeCVgACOtZqA8WrcS4w==}
/@dydxprotocol/v4-abacus@1.0.24:
resolution: {integrity: sha512-wDGSjkrc3Se6Ev7UTjPgJV7PiyzZSz2mJwOTZikLwH7W3k1iPYGQmaCd7TudFk8h2aSdlEwNsQBCf0sLoyvHaQ==}
dev: false
/@dydxprotocol/v4-client-js@1.0.0:
+9 -5
View File
@@ -44,6 +44,13 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
}: ComboboxMenuProps<MenuItemValue, MenuGroupValue>) => {
const [highlightedCommand, setHighlightedCommand] = useState<MenuItemValue>();
const [searchValue, setSearchValue] = useState('');
// const inputRef = useRef<HTMLInputElement | null>(null);
// console.log({ commandValue: highlightedCommand });
// useEffect(() => {
// inputRef?.current?.focus();
// }, []);
return (
<Styled.Command
@@ -60,14 +67,11 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
{withSearch && (
<Styled.Header $withStickyLayout={withStickyLayout}>
<Styled.Input
/**
* Mobile Issue: Search Input will always trigger mobile keyboard drawer. There is no fix.
* https://github.com/pacocoursey/cmdk/issues/127
*/
autoFocus
// ref={inputRef}
type="search"
value={searchValue}
onValueChange={setSearchValue}
autoFocus
placeholder={inputPlaceholder}
/>
</Styled.Header>
+2 -7
View File
@@ -4,7 +4,6 @@ import { Root, Action, Close } from '@radix-ui/react-toast';
import { ButtonShape, ButtonSize } from '@/constants/buttons';
import { popoverMixins } from '@/styles/popoverMixins';
import { breakpoints } from '@/styles';
import { Notification, type NotificationProps } from '@/components/Notification';
@@ -77,7 +76,7 @@ export const Toast = ({
<Close asChild>
<$CloseButton
iconName={IconName.Close}
shape={ButtonShape.Circle}
shape={ButtonShape.Square}
size={ButtonSize.XSmall}
onClick={(e: MouseEvent) => e.stopPropagation()}
/>
@@ -250,16 +249,12 @@ const $CloseButton = styled(IconButton)`
right: 0;
border: solid var(--border-width) var(--color-border);
border-radius: 50%;
${$Root}:hover & {
display: block;
z-index: 2;
}
@media ${breakpoints.mobile} {
display: block;
z-index: 2;
}
`;
const $Action = styled(Action)`
+2 -5
View File
@@ -1,7 +1,6 @@
import styled from 'styled-components';
import { Provider, Viewport } from '@radix-ui/react-toast';
import styled from 'styled-components';
import { layoutMixins } from '@/styles/layoutMixins';
type ElementProps = {
@@ -13,9 +12,7 @@ type StyleProps = {
className?: string;
};
type ToastAreaProps = ElementProps & StyleProps;
export const ToastArea = ({ swipeDirection, children, className }: ToastAreaProps) => (
export const ToastArea = ({ swipeDirection, children, className }: ElementProps & StyleProps) => (
<$ToastArea className={className}>
<Provider swipeDirection={swipeDirection}>
{children}
-12
View File
@@ -116,15 +116,3 @@ export enum MobilePlaceOrderSteps {
PlacingOrder = 'PlacingOrder',
Confirmation = 'Confirmation',
}
export const CLEARED_TRADE_INPUTS = {
limitPriceInput: '',
triggerPriceInput: '',
trailingPercentInput: '',
};
export const CLEARED_SIZE_INPUTS = {
amountInput: '',
usdAmountInput: '',
leverageInput: '',
};
-2
View File
@@ -22,7 +22,6 @@ import { useShouldShowFooter } from './useShouldShowFooter';
import { useSelectedNetwork } from './useSelectedNetwork';
import { useStringGetter } from './useStringGetter';
import { useSubaccount } from './useSubaccount';
import { useTradeFormInputs } from './useTradeFormInputs';
import { useURLConfigs } from './useURLConfigs';
export {
@@ -50,6 +49,5 @@ export {
useSelectedNetwork,
useStringGetter,
useSubaccount,
useTradeFormInputs,
useURLConfigs,
};
-1
View File
@@ -23,7 +23,6 @@ export const useCurrentMarketId = () => {
const selectedNetwork = useSelector(getSelectedNetwork);
const marketIds = useSelector(getMarketIds, shallowEqual);
const hasMarketIds = marketIds.length > 0;
const [lastViewedMarket, setLastViewedMarket] = useLocalStorage({
key: LocalStorageKey.LastViewedMarket,
defaultValue: DEFAULT_MARKETID,
-33
View File
@@ -1,33 +0,0 @@
import { getTradeFormInputs } from '@/state/inputsSelectors';
import { useEffect } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { TradeInputField } from '@/constants/abacus';
import abacusStateManager from '@/lib/abacus';
export const useTradeFormInputs = () => {
const tradeFormInputValues = useSelector(getTradeFormInputs, shallowEqual);
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
useEffect(() => {
const floatValue = parseFloat(triggerPriceInput);
abacusStateManager.setTradeValue({
value: floatValue,
field: TradeInputField.triggerPrice,
});
}, [triggerPriceInput]);
useEffect(() => {
const floatValue = parseFloat(limitPriceInput);
abacusStateManager.setTradeValue({ value: floatValue, field: TradeInputField.limitPrice });
}, [limitPriceInput]);
useEffect(() => {
const floatValue = parseFloat(trailingPercentInput);
abacusStateManager.setTradeValue({
value: floatValue,
field: TradeInputField.trailingPercent,
});
}, [trailingPercentInput]);
};
+14 -77
View File
@@ -1,17 +1,15 @@
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import styled, { css } from 'styled-components';
import { NotificationStatus } from '@/constants/notifications';
import { ButtonShape, ButtonSize } from '@/constants/buttons';
import { ButtonSize } from '@/constants/buttons';
import { useNotifications } from '@/hooks/useNotifications';
import { useBreakpoints } from '@/hooks/useBreakpoints';
import { ChevronLeftIcon } from '@/icons';
import { breakpoints } from '@/styles';
import { Button } from '@/components/Button';
import { Toast } from '@/components/Toast';
import { ToastArea } from '@/components/ToastArea';
import { ToggleButton } from '@/components/ToggleButton';
type StyleProps = {
className?: string;
@@ -20,8 +18,6 @@ type StyleProps = {
const MAX_TOASTS = 10;
export const NotificationsToastArea = ({ className }: StyleProps) => {
const [shouldStackNotifications, setshouldStackNotifications] = useState(true);
const {
notifications,
getKey,
@@ -47,37 +43,20 @@ export const NotificationsToastArea = ({ className }: StyleProps) => {
key: getKey(notification),
displayData: getDisplayData(notification),
}))
.filter(
({ displayData, notification }) =>
displayData && notification.status < NotificationStatus.Unseen
)
.filter(({ displayData }) => displayData)
.slice(-MAX_TOASTS)
);
}, [notifications, getKey, getDisplayData]);
if (isMenuOpen) return null;
const hasMultipleToasts = notificationMap.length > 1;
return (
<StyledToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}>
{hasMultipleToasts && (
<StyledToggleButton
shape={ButtonShape.Pill}
size={ButtonSize.XSmall}
isPressed={shouldStackNotifications}
onPressedChange={() => setshouldStackNotifications(!shouldStackNotifications)}
>
<ChevronLeftIcon />
</StyledToggleButton>
)}
{notificationMap.map(({ notification, key, displayData }, idx) => (
<StyledToast
key={key}
isStacked={shouldStackNotifications}
isOpen={notification.status < NotificationStatus.Unseen}
layer={notificationMap.length - 1 - idx}
isOpen={notification.status < NotificationStatus.Unseen}
notification={notification}
slotIcon={displayData.icon}
slotTitle={displayData.title}
@@ -95,7 +74,7 @@ export const NotificationsToastArea = ({ className }: StyleProps) => {
actionAltText={displayData.actionAltText}
duration={displayData.toastDuration ?? Infinity}
sensitivity={displayData.toastSensitivity}
setIsOpen={(isOpen: boolean, isClosedFromTimeout?: boolean) => {
setIsOpen={(isOpen, isClosedFromTimeout) => {
if (!isOpen)
if (isClosedFromTimeout)
// Toast timer expired without user interaction
@@ -118,58 +97,16 @@ const StyledToastArea = styled(ToastArea)`
padding: 0.75rem 0.75rem 0.75rem 0;
mask-image: linear-gradient(to left, transparent, white 0.5rem);
@media ${breakpoints.mobile} {
width: 100%;
position: fixed;
}
`;
const StyledToast = styled(Toast)<{ isStacked?: boolean; layer: number }>`
const StyledToast = styled(Toast)<{ layer: number }>`
// Stacked toast
${({ isStacked, layer }) =>
isStacked
? css`
position: absolute;
left: 0;
top: calc(${layer} * 2px);
right: calc(${layer} * -2px);
`
: css`
margin-bottom: 0.75rem;
`}
`;
const StyledToggleButton = styled(ToggleButton)<{ isPressed: boolean }>`
z-index: 2;
pointer-events: auto;
display: none;
position: absolute;
top: 4px;
left: calc(50% - 0.75rem);
--button-width: 2rem;
--button-height: 1rem;
${StyledToastArea}:hover & {
display: flex;
}
> svg {
width: 4px;
margin-top: 1px;
transform: rotate(-90deg);
${({ isPressed }) =>
!isPressed &&
css`
transform: rotate(90deg);
`}
}
@media ${breakpoints.mobile} {
display: flex;
left: calc(50% - 2rem);
--button-width: 4rem;
--button-height: 2rem;
}
top: 0;
left: 0;
right: 0;
${({ layer }) => css`
right: calc(${layer} * -2px);
top: calc(${layer} * 2px);
`}
`;
+3 -8
View File
@@ -27,11 +27,11 @@ import {
import { DEFAULT_MARKETID } from '@/constants/markets';
import { CURRENT_ABACUS_DEPLOYMENT, type DydxNetwork } from '@/constants/networks';
import { CLEARED_SIZE_INPUTS, CLEARED_TRADE_INPUTS } from '@/constants/trade';
import type { RootStore } from '@/state/_store';
import { setTradeFormInputs } from '@/state/inputs';
import { getInputTradeOptions, getTransferInputs } from '@/state/inputsSelectors';
import { getInputTradeOptions } from '@/state/inputsSelectors';
import { getTransferInputs } from '@/state/inputsSelectors';
import AbacusRest from './rest';
import AbacusAnalytics from './analytics';
@@ -43,7 +43,6 @@ import AbacusFormatter from './formatter';
import AbacusThreading from './threading';
import AbacusFileSystem from './filesystem';
import { LocaleSeparators } from '../numbers';
class AbacusStateManager {
private store: RootStore | undefined;
private currentMarket: string | undefined;
@@ -133,8 +132,6 @@ class AbacusStateManager {
this.setTradeValue({ value: null, field: TradeInputField.limitPrice });
}
this.store?.dispatch(setTradeFormInputs(CLEARED_TRADE_INPUTS));
if (shouldResetSize) {
this.setTradeValue({ value: null, field: TradeInputField.size });
this.setTradeValue({ value: null, field: TradeInputField.usdcSize });
@@ -142,8 +139,6 @@ class AbacusStateManager {
if (needsLeverage) {
this.setTradeValue({ value: null, field: TradeInputField.leverage });
}
this.store?.dispatch(setTradeFormInputs(CLEARED_SIZE_INPUTS));
}
};
+1 -7
View File
@@ -7,12 +7,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { TradeLayouts } from '@/constants/layout';
import {
useBreakpoints,
useCurrentMarketId,
usePageTitlePriceUpdates,
useTradeFormInputs,
} from '@/hooks';
import { useBreakpoints, useCurrentMarketId, usePageTitlePriceUpdates } from '@/hooks';
import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSelectedTradeLayout } from '@/state/layoutSelectors';
@@ -42,7 +37,6 @@ const TradePage = () => {
const [isHorizontalPanelOpen, setIsHorizontalPanelOpen] = useState(true);
usePageTitlePriceUpdates();
useTradeFormInputs();
return isTablet ? (
<Styled.TradeLayoutMobile>
+1 -15
View File
@@ -1,5 +1,4 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import assign from 'lodash/assign';
import type {
InputError,
@@ -10,14 +9,9 @@ import type {
TransferInputs,
} from '@/constants/abacus';
import { CLEARED_SIZE_INPUTS, CLEARED_TRADE_INPUTS } from '@/constants/trade';
type TradeFormInputs = typeof CLEARED_TRADE_INPUTS & typeof CLEARED_SIZE_INPUTS;
export interface InputsState {
current?: Nullable<string>;
inputErrors?: Nullable<InputError[]>;
tradeFormInputs: TradeFormInputs;
tradeInputs?: Nullable<TradeInputs>;
closePositionInputs?: Nullable<ClosePositionInputs>;
transferInputs?: Nullable<TransferInputs>;
@@ -26,10 +20,6 @@ export interface InputsState {
const initialState: InputsState = {
current: undefined,
inputErrors: undefined,
tradeFormInputs: {
...CLEARED_TRADE_INPUTS,
...CLEARED_SIZE_INPUTS,
},
tradeInputs: undefined,
transferInputs: undefined,
};
@@ -50,11 +40,7 @@ export const inputsSlice = createSlice({
transferInputs: transfer,
};
},
setTradeFormInputs: (state, action: PayloadAction<Partial<TradeFormInputs>>) => {
state.tradeFormInputs = assign({}, state.tradeFormInputs, action.payload);
},
},
});
export const { setInputs, setTradeFormInputs } = inputsSlice.actions;
export const { setInputs } = inputsSlice.actions;
-5
View File
@@ -120,8 +120,3 @@ export const useTradeFormData = () => {
shallowEqual
);
};
/**
* @returns Tradeform Input states for display. Abacus inputs should track these values.
*/
export const getTradeFormInputs = (state: RootState) => state.inputs.tradeFormInputs;
+2 -3
View File
@@ -5,7 +5,7 @@ import { createSelector } from 'reselect';
import { TradeInputField } from '@/constants/abacus';
import { TradeTypes } from '@/constants/trade';
import { STRING_KEYS, StringKey } from '@/constants/localization';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -30,8 +30,7 @@ const useTradeTypeOptions = () => {
const allTradeTypeItems = typeOptions?.toArray()?.map(({ type, stringKey }) => ({
value: type,
label: stringGetter({
key:
type === TradeTypes.TAKE_PROFIT ? STRING_KEYS.TAKE_PROFIT_LIMIT : (stringKey as StringKey),
key: type === TradeTypes.TAKE_PROFIT ? STRING_KEYS.TAKE_PROFIT_LIMIT : stringKey,
}),
}));
@@ -7,7 +7,6 @@ import { STRING_KEYS } from '@/constants/localization';
import { ButtonAction } from '@/constants/buttons';
import { layoutMixins } from '@/styles/layoutMixins';
import { formMixins } from '@/styles/formMixins';
import { Button } from '@/components/Button';
import { Link } from '@/components/Link';
@@ -55,14 +54,14 @@ export const AcknowledgeTerms = ({ onClose, onContinue }: ElementProps) => {
<li>{stringGetter({ key: STRING_KEYS.TOS_LINE5 })}</li>
</ul>
</Styled.TOS>
<Styled.Footer>
<Styled.Row>
<Styled.Button onClick={onClose} action={ButtonAction.Base}>
{stringGetter({ key: STRING_KEYS.CLOSE })}
</Styled.Button>
<Styled.Button onClick={onAcknowledgement} action={ButtonAction.Primary}>
{stringGetter({ key: STRING_KEYS.I_AGREE })}
</Styled.Button>
</Styled.Footer>
</Styled.Row>
</>
);
};
@@ -88,9 +87,8 @@ Styled.TOS = styled.section`
}
`;
Styled.Footer = styled.div`
${formMixins.footer};
${layoutMixins.row}
Styled.Row = styled.div`
${layoutMixins.row};
gap: 1rem;
`;
+20 -19
View File
@@ -1,6 +1,6 @@
import { type FormEvent, useState, Ref, useCallback, useEffect } from 'react';
import { type FormEvent, useState, Ref, useCallback } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { shallowEqual, useSelector } from 'react-redux';
import type { NumberFormatValues, SourceInfo } from 'react-number-format';
import { AlertType } from '@/constants/alerts';
@@ -37,8 +37,7 @@ import { WithTooltip } from '@/components/WithTooltip';
import { Orderbook } from '@/views/tables/Orderbook';
import { setTradeFormInputs } from '@/state/inputs';
import { getCurrentInput, getTradeFormInputs, useTradeFormData } from '@/state/inputsSelectors';
import { getCurrentInput, useTradeFormData } from '@/state/inputsSelectors';
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus';
@@ -81,7 +80,6 @@ export const TradeForm = ({
const [placeOrderError, setPlaceOrderError] = useState<string>();
const [showOrderbook, setShowOrderbook] = useState(false);
const dispatch = useDispatch();
const stringGetter = useStringGetter();
const { placeOrder } = useSubaccount();
@@ -100,21 +98,18 @@ export const TradeForm = ({
tradeErrors,
} = useTradeFormData();
const { limitPrice, triggerPrice, trailingPercent } = price || {};
const currentInput = useSelector(getCurrentInput);
const { tickSizeDecimals, stepSizeDecimals } =
useSelector(getCurrentMarketConfig, shallowEqual) || {};
const tradeFormInputValues = useSelector(getTradeFormInputs, shallowEqual);
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
const needsAdvancedOptions =
needsGoodUntil || timeInForceOptions || executionOptions || needsPostOnly || needsReduceOnly;
const tradeFormInputs: TradeBoxInputConfig[] = [];
const isInputFilled =
Object.values(tradeFormInputValues).some((val) => val !== '') ||
Object.values(price || {}).some((val) => !!val) ||
Object.values(price || {}).some((val) => val != null) ||
[size?.size, size?.usdcSize, size?.leverage].some((val) => val != null);
const hasInputErrors =
@@ -202,10 +197,13 @@ export const TradeForm = ({
<Tag>USD</Tag>
</>
),
onChange: ({ value }: NumberFormatValues) => {
dispatch(setTradeFormInputs({ triggerPriceInput: value }));
onChange: ({ floatValue }: NumberFormatValues) => {
abacusStateManager.setTradeValue({
value: floatValue,
field: TradeInputField.triggerPrice,
});
},
value: triggerPriceInput ?? '',
value: triggerPrice ?? '',
decimals: tickSizeDecimals || USD_DECIMALS,
});
}
@@ -222,10 +220,10 @@ export const TradeForm = ({
<Tag>USD</Tag>
</>
),
onChange: ({ value }: NumberFormatValues) => {
dispatch(setTradeFormInputs({ limitPriceInput: value }));
onChange: ({ floatValue }: NumberFormatValues) => {
abacusStateManager.setTradeValue({ value: floatValue, field: TradeInputField.limitPrice });
},
value: limitPriceInput,
value: limitPrice ?? '',
decimals: tickSizeDecimals || USD_DECIMALS,
});
}
@@ -239,10 +237,13 @@ export const TradeForm = ({
{stringGetter({ key: STRING_KEYS.TRAILING_PERCENT })}
</WithTooltip>
),
onChange: ({ value }: NumberFormatValues) => {
dispatch(setTradeFormInputs({ trailingPercentInput: value }));
onChange: ({ floatValue }: NumberFormatValues) => {
abacusStateManager.setTradeValue({
value: floatValue,
field: TradeInputField.trailingPercent,
});
},
value: trailingPercentInput ?? '',
value: trailingPercent ?? '',
});
}
@@ -30,8 +30,8 @@ import { getSelectedOrderSide, hasPositionSideChanged } from '@/lib/tradeData';
import { LeverageSlider } from './LeverageSlider';
type ElementProps = {
leverageInputValue: string;
setLeverageInputValue: (value: string) => void;
leverageInputValue: Nullable<number>;
setLeverageInputValue: Dispatch<SetStateAction<Nullable<number>>>;
};
export const MarketLeverageInput = ({
@@ -66,7 +66,7 @@ export const MarketLeverageInput = ({
const newLeverage = MustBigNumber(floatValue).toFixed();
if (value === '' || newLeverage === 'NaN' || !floatValue) {
setLeverageInputValue('');
setLeverageInputValue(null);
abacusStateManager.setTradeValue({
value: null,
field: TradeInputField.leverage,
@@ -84,7 +84,7 @@ export const MarketLeverageInput = ({
? newLeverageBN.abs().negated()
: newLeverageBN.abs();
setLeverageInputValue(newLeverageSignedBN.toString());
setLeverageInputValue(newLeverageSignedBN.toNumber());
abacusStateManager.setTradeValue({
value: newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS),
@@ -98,11 +98,11 @@ export const MarketLeverageInput = ({
if (leveragePosition === PositionSide.None) return;
const inputValue = leverageInputValue || currentLeverage;
const newInputValue = MustBigNumber(inputValue).negated().toFixed(LEVERAGE_DECIMALS);
const newInputValue = MustBigNumber(inputValue).negated().toNumber();
setLeverageInputValue(newInputValue);
abacusStateManager.setTradeValue({
value: newInputValue,
value: newInputValue.toFixed(LEVERAGE_DECIMALS),
field: TradeInputField.leverage,
});
};
+18 -24
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components';
import { TradeInputField } from '@/constants/abacus';
@@ -20,13 +20,8 @@ import { Icon, IconName } from '@/components/Icon';
import { ToggleButton } from '@/components/ToggleButton';
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { setTradeFormInputs } from '@/state/inputs';
import {
getInputTradeSizeData,
getInputTradeOptions,
getTradeFormInputs,
} from '@/state/inputsSelectors';
import { getInputTradeSizeData, getInputTradeOptions } from '@/state/inputsSelectors';
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
@@ -36,8 +31,12 @@ import { MustBigNumber } from '@/lib/numbers';
import { MarketLeverageInput } from './MarketLeverageInput';
export const TradeSizeInputs = () => {
const [sizeInputValue, setSizeInputValue] = useState<number | null>();
const [usdcInputValue, setUsdcInputValue] = useState<number | null>();
const [leverageInputValue, setLeverageInputValue] = useState<number | null>();
const [showUSDCInputOnTablet, setShowUSDCInputOnTablet] = useState(false);
const dispatch = useDispatch();
const stringGetter = useStringGetter();
const { isTablet } = useBreakpoints();
@@ -51,27 +50,24 @@ export const TradeSizeInputs = () => {
const { needsLeverage } = currentTradeInputOptions || {};
const decimals = stepSizeDecimals || TOKEN_DECIMALS;
const { amountInput, usdAmountInput, leverageInput } = useSelector(
getTradeFormInputs,
shallowEqual
);
// Update State variables if their inputs are not being source of calculations
// Or if they have been reset to null
useEffect(() => {
if (lastEditedInput !== TradeSizeInput.Size || size == null) {
dispatch(setTradeFormInputs({ amountInput: size ? size.toString() : '' }));
// Abacus size already respects step size
// using .toFixed to prevent trailing zeros and exponential notation
setSizeInputValue(size);
}
if (lastEditedInput !== TradeSizeInput.Usdc || usdcSize == null) {
dispatch(setTradeFormInputs({ usdAmountInput: usdcSize ? usdcSize.toString() : '' }));
setUsdcInputValue(usdcSize);
}
if (lastEditedInput !== TradeSizeInput.Leverage || leverage == null) {
dispatch(setTradeFormInputs({ leverageInput: leverage ? leverage.toString() : '' }));
setLeverageInputValue(leverage);
}
}, [size, usdcSize, leverage, lastEditedInput]);
const onSizeInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
dispatch(setTradeFormInputs({ amountInput: value }));
setSizeInputValue(floatValue);
const newAmount = MustBigNumber(floatValue).toFixed(decimals);
abacusStateManager.setTradeValue({
@@ -81,7 +77,7 @@ export const TradeSizeInputs = () => {
};
const onUSDCInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
dispatch(setTradeFormInputs({ usdAmountInput: value }));
setUsdcInputValue(floatValue);
const newUsdcAmount = MustBigNumber(floatValue).toFixed();
abacusStateManager.setTradeValue({
@@ -117,7 +113,7 @@ export const TradeSizeInputs = () => {
}
slotRight={isTablet && inputToggleButton}
type={InputType.Number}
value={amountInput || ''}
value={sizeInputValue || ''}
/>
);
@@ -126,7 +122,7 @@ export const TradeSizeInputs = () => {
id="trade-usdc"
onInput={onUSDCInput}
type={InputType.Currency}
value={usdAmountInput || ''}
value={usdcInputValue || ''}
decimals={tickSizeDecimals || USD_DECIMALS}
label={
<>
@@ -157,10 +153,8 @@ export const TradeSizeInputs = () => {
{needsLeverage && (
<MarketLeverageInput
leverageInputValue={leverageInput}
setLeverageInputValue={(value: string) =>
dispatch(setTradeFormInputs({ leverageInput: value }))
}
leverageInputValue={leverageInputValue}
setLeverageInputValue={setLeverageInputValue}
/>
)}
</Styled.Column>
+4 -4
View File
@@ -96,19 +96,19 @@ export const MarketsTable = ({ className }: { className?: string }) => {
),
},
{
columnKey: 'priceChange24HPercent',
getCellValue: (row) => row.priceChange24HPercent,
columnKey: 'priceChange24H',
getCellValue: (row) => row.priceChange24H,
label: stringGetter({ key: STRING_KEYS.CHANGE_24H }),
renderCell: ({ priceChange24H, priceChange24HPercent, tickSizeDecimals }) => (
<TableCell stacked>
<Styled.InlineRow>
{!priceChange24HPercent ? (
{!priceChange24H ? (
<Output type={OutputType.Text} value={null} />
) : (
<Styled.Output
type={OutputType.Percent}
value={MustBigNumber(priceChange24HPercent).abs()}
isNegative={MustBigNumber(priceChange24HPercent).isNegative()}
isNegative={MustBigNumber(priceChange24H).isNegative()}
/>
)}
</Styled.InlineRow>
+20 -22
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css, keyframes } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js';
@@ -9,13 +9,13 @@ import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks';
import { calculateCanViewAccount } from '@/state/accountCalculators';
import { setTradeFormInputs } from '@/state/inputs';
import { getSubaccountOpenOrdersBySideAndPrice } from '@/state/accountSelectors';
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { getCurrentMarketConfig, getCurrentMarketOrderbook } from '@/state/perpetualsSelectors';
import { getCurrentInput } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus';
import { MustBigNumber } from '@/lib/numbers';
import { Details } from '@/components/Details';
@@ -208,6 +208,7 @@ const OrderbookTable = ({
// Style props
histogramSide={histogramSide}
hideHeader={hideHeader}
withFocusStickyRows
style={{
'--histogram-range': histogramRange,
}}
@@ -222,12 +223,11 @@ export const Orderbook = ({
maxRowsPerSide = ORDERBOOK_MAX_ROWS_PER_SIDE,
hideHeader = false,
}: ElementProps & StyleProps) => {
const dispatch = useDispatch();
const stringGetter = useStringGetter();
const { isTablet } = useBreakpoints();
const currentInput = useSelector(getCurrentInput);
const { id = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const { symbol = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const { stepSizeDecimals, tickSizeDecimals } =
useSelector(getCurrentMarketConfig, shallowEqual) ?? {};
@@ -274,16 +274,18 @@ export const Orderbook = ({
const onRowAction = useCallback(
(key: string, row: RowData) => {
if (currentInput === 'trade' && key !== 'spread' && row?.price) {
dispatch(setTradeFormInputs({ limitPriceInput: row?.price?.toString() }));
}
if (currentInput === 'trade' && key !== 'spread' && row?.price)
abacusStateManager.setTradeValue({
value: row?.price,
field: TradeInputField.limitPrice,
});
},
[currentInput]
);
const orderbookTableProps = {
showMineColumn,
symbol: id,
symbol,
stepSizeDecimals,
tickSizeDecimals,
histogramRange,
@@ -508,21 +510,17 @@ Styled.OrderbookTable = styled(OrderbookTradesTable)<StyleProps>`
content: '';
}
${({ withFocusStickyRows }) =>
withFocusStickyRows &&
css`
&:not(:active):is(:focus-visible, :focus-within) {
${orderbookMixins.scrollSnapItem}
z-index: 2;
&:not(:active):is(:focus-visible, :focus-within) {
${orderbookMixins.scrollSnapItem}
z-index: 2;
&[data-side='bid'] {
top: calc(var(--stickyArea-totalInsetTop) + var(--orderbook-spreadRowHeight));
}
&[data-side='ask'] {
bottom: calc(var(--stickyArea-totalInsetBottom) + var(--orderbook-spreadRowHeight));
}
}
`}
&[data-side='bid'] {
top: calc(var(--stickyArea-totalInsetTop) + var(--orderbook-spreadRowHeight));
}
&[data-side='ask'] {
bottom: calc(var(--stickyArea-totalInsetBottom) + var(--orderbook-spreadRowHeight));
}
}
}
${Styled.HorizontalLayout} & {