navigate(AppRoute.Markets)} />
}
- subtitle={isNotTablet && stringGetter({ key: STRING_KEYS.ADD_DETAILS_TO_LAUNCH_MARKET })}
+ subtitle={isNotTablet && stringGetter({ key: STRING_KEYS.LISTINGS_DESCRIPTION })}
/>
$HeaderSection>
<$Content>
diff --git a/src/pages/portfolio/AccountDetailsAndHistory.tsx b/src/pages/portfolio/AccountDetailsAndHistory.tsx
index 2745a6ca2..0c7c83ddf 100644
--- a/src/pages/portfolio/AccountDetailsAndHistory.tsx
+++ b/src/pages/portfolio/AccountDetailsAndHistory.tsx
@@ -10,6 +10,7 @@ import { OnboardingState } from '@/constants/account';
import { ComplianceStates } from '@/constants/compliance';
import { STRING_KEYS } from '@/constants/localization';
import { NumberSign } from '@/constants/numbers';
+import { AppRoute, BASE_ROUTE } from '@/constants/routes';
import { useBreakpoints } from '@/hooks/useBreakpoints';
import { useComplianceState } from '@/hooks/useComplianceState';
@@ -18,6 +19,7 @@ import { useStringGetter } from '@/hooks/useStringGetter';
import breakpoints from '@/styles/breakpoints';
import { layoutMixins } from '@/styles/layoutMixins';
+import { Link } from '@/components/Link';
import { Output, OutputType, ShowSign } from '@/components/Output';
import { TriangleIndicator } from '@/components/TriangleIndicator';
import { WithLabel } from '@/components/WithLabel';
@@ -180,7 +182,18 @@ export const AccountDetailsAndHistory = () => {
slotEmpty={
<$EmptyChart>
{complianceState === ComplianceStates.READ_ONLY ? (
- <$EmptyCard>{stringGetter({ key: STRING_KEYS.BLOCKED_MESSAGE })}$EmptyCard>
+ <$Card>
+ {stringGetter({
+ key: STRING_KEYS.BLOCKED_MESSAGE,
+ params: {
+ TERMS_OF_USE_LINK: (
+ <$Link href={`${BASE_ROUTE}${AppRoute.Terms}`} withIcon>
+ {stringGetter({ key: STRING_KEYS.TERMS_OF_USE })}
+ $Link>
+ ),
+ },
+ })}
+ $Card>
) : onboardingState !== OnboardingState.AccountConnected ? (
<$EmptyCard>
@@ -293,21 +306,28 @@ const $EmptyChart = styled.div`
cursor: default;
`;
-const $EmptyCard = styled.div`
- width: 16.75rem;
-
- ${layoutMixins.column};
- font: var(--font-base-book);
- gap: 1rem;
-
+const $Card = styled.div`
padding: 1.25rem;
margin: auto;
background-color: var(--color-layer-3);
border-radius: 0.5rem;
- text-align: center;
justify-items: center;
+ width: 16.75rem;
+ font: var(--font-base-book);
+`;
+
+const $EmptyCard = styled($Card)`
+ ${layoutMixins.column};
+
+ display: grid;
+ gap: 1rem;
button {
width: fit-content;
}
`;
+
+const $Link = styled(Link)`
+ ${layoutMixins.inlineRow};
+ text-decoration: underline;
+`;
diff --git a/src/pages/portfolio/Portfolio.tsx b/src/pages/portfolio/Portfolio.tsx
index 49b623401..f1673942e 100644
--- a/src/pages/portfolio/Portfolio.tsx
+++ b/src/pages/portfolio/Portfolio.tsx
@@ -230,7 +230,7 @@ const PortfolioPage = () => {
{complianceState === ComplianceStates.FULL_ACCESS && (
@@ -238,7 +238,7 @@ const PortfolioPage = () => {
{usdcBalance > 0 && (
@@ -247,7 +247,7 @@ const PortfolioPage = () => {
(usdcBalance > 0 || nativeTokenBalance.gt(0)) && (
diff --git a/src/pages/token/rewards/DYDXBalancePanel.tsx b/src/pages/token/rewards/DYDXBalancePanel.tsx
index 0647e4ac2..458c12bfd 100644
--- a/src/pages/token/rewards/DYDXBalancePanel.tsx
+++ b/src/pages/token/rewards/DYDXBalancePanel.tsx
@@ -54,7 +54,7 @@ export const DYDXBalancePanel = ({ className }: { className?: string }) => {
slotLeft={}
size={ButtonSize.Small}
action={ButtonAction.Primary}
- onClick={() => dispatch(openDialog({ type: DialogTypes.Transfer }))}
+ onClick={() => dispatch(openDialog(DialogTypes.Transfer({})))}
>
{stringGetter({ key: STRING_KEYS.TRANSFER })}
diff --git a/src/pages/token/rewards/GovernancePanel.tsx b/src/pages/token/rewards/GovernancePanel.tsx
index fd921fa57..713c7de56 100644
--- a/src/pages/token/rewards/GovernancePanel.tsx
+++ b/src/pages/token/rewards/GovernancePanel.tsx
@@ -20,7 +20,7 @@ export const GovernancePanel = ({ className }: { className?: string }) => {
const { governanceLearnMore } = useURLConfigs();
const openKeplrDialog = useCallback(
- () => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr })),
+ () => dispatch(openDialog(DialogTypes.ExternalNavKeplr())),
[dispatch]
);
diff --git a/src/pages/token/rewards/LaunchIncentivesPanel.tsx b/src/pages/token/rewards/LaunchIncentivesPanel.tsx
index ae2eccd97..5c4e477ab 100644
--- a/src/pages/token/rewards/LaunchIncentivesPanel.tsx
+++ b/src/pages/token/rewards/LaunchIncentivesPanel.tsx
@@ -155,10 +155,9 @@ const LaunchIncentivesContent = () => {
action={ButtonAction.Base}
onClick={() => {
dispatch(
- openDialog({
- type: DialogTypes.ExternalLink,
- dialogProps: { link: 'https://dydx.exchange/blog/v4-full-trading' },
- })
+ openDialog(
+ DialogTypes.ExternalLink({ link: 'https://dydx.exchange/blog/v4-full-trading' })
+ )
);
}}
slotRight={}
@@ -169,10 +168,11 @@ const LaunchIncentivesContent = () => {
action={ButtonAction.Primary}
onClick={() => {
dispatch(
- openDialog({
- type: DialogTypes.ExternalLink,
- dialogProps: { link: 'https://community.chaoslabs.xyz/dydx-v4/risk/leaderboard' },
- })
+ openDialog(
+ DialogTypes.ExternalLink({
+ link: 'https://community.chaoslabs.xyz/dydx-v4/risk/leaderboard',
+ })
+ )
);
}}
slotRight={}
diff --git a/src/pages/token/rewards/RewardsHelpPanel.tsx b/src/pages/token/rewards/RewardsHelpPanel.tsx
index a47c6cb9f..686d07faf 100644
--- a/src/pages/token/rewards/RewardsHelpPanel.tsx
+++ b/src/pages/token/rewards/RewardsHelpPanel.tsx
@@ -1,6 +1,5 @@
import styled from 'styled-components';
-import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useEnvFeatures } from '@/hooks/useEnvFeatures';
@@ -14,23 +13,11 @@ import { Accordion } from '@/components/Accordion';
import { Link } from '@/components/Link';
import { Panel } from '@/components/Panel';
-import { useAppDispatch } from '@/state/appTypes';
-import { openDialog } from '@/state/dialogs';
-
export const RewardsHelpPanel = () => {
- const dispatch = useAppDispatch();
const stringGetter = useStringGetter();
const { isStakingEnabled } = useEnvFeatures();
- const { tradingRewardsLearnMore, mintscanValidatorsLearnMore, validatorSelectionDocument } =
- useURLConfigs();
-
- const openKeplrDialog = () =>
- dispatch(
- openDialog({
- type: DialogTypes.ExternalNavKeplr,
- })
- );
+ const { protocolStaking, tradingRewardsLearnMore } = useURLConfigs();
return (
<$HelpCard
@@ -76,7 +63,7 @@ export const RewardsHelpPanel = () => {
key: STRING_KEYS.FAQ_WHAT_IS_STAKING_ANSWER,
params: {
HERE_LINK: (
- <$AccentLink href="https://protocolstaking.info/">
+ <$AccentLink href={protocolStaking}>
{stringGetter({ key: STRING_KEYS.HERE })}
$AccentLink>
),
@@ -95,41 +82,6 @@ export const RewardsHelpPanel = () => {
key: STRING_KEYS.FAQ_WHAT_ARE_THE_RISKS_OF_STAKING_ANSWER,
}),
},
- {
- header: stringGetter({
- key: STRING_KEYS.FAQ_HOW_IS_THE_PRECONFIGURED_SET_OF_VALIDATORS_DETERMINED_QUESTION,
- }),
- content: stringGetter({
- key: STRING_KEYS.FAQ_HOW_IS_THE_PRECONFIGURED_SET_OF_VALIDATORS_DETERMINED_ANSWER,
- params: {
- DOCUMENT_LINK: (
- <$AccentLink href={mintscanValidatorsLearnMore}>
- {stringGetter({ key: STRING_KEYS.DOCUMENT })}
- $AccentLink>
- ),
- },
- }),
- },
- {
- header: stringGetter({
- key: STRING_KEYS.FAQ_WHICH_VALIDATORS_ARE_AVAILABLE_QUESTION,
- }),
- content: stringGetter({
- key: STRING_KEYS.FAQ_WHICH_VALIDATORS_ARE_AVAILABLE_ANSWER,
- params: {
- KEPLR_LINK: (
- <$AccentLink onClick={openKeplrDialog}>
- {stringGetter({ key: STRING_KEYS.KEPLR })}
- $AccentLink>
- ),
- DOCUMENT_LINK: (
- <$AccentLink href={validatorSelectionDocument}>
- {stringGetter({ key: STRING_KEYS.DOCUMENT })}
- $AccentLink>
- ),
- },
- }),
- },
]
: []),
]}
diff --git a/src/pages/token/rewards/StakingPanel.tsx b/src/pages/token/rewards/StakingPanel.tsx
index 2d414ef71..ae2896eb3 100644
--- a/src/pages/token/rewards/StakingPanel.tsx
+++ b/src/pages/token/rewards/StakingPanel.tsx
@@ -58,6 +58,11 @@ export const StakingPanel = ({ className }: { className?: string }) => {
},
});
+ const aprText = stringGetter({
+ key: STRING_KEYS.EST_APR,
+ params: { PERCENTAGE: <$Output type={OutputType.Percent} value={stakingApr} /> },
+ });
+
return (
{
slotLeft={}
size={ButtonSize.Small}
action={ButtonAction.Primary}
- onClick={() => dispatch(openDialog({ type: DialogTypes.Transfer }))}
+ onClick={() => dispatch(openDialog(DialogTypes.Transfer({})))}
>
{stringGetter({ key: STRING_KEYS.TRANSFER })}
@@ -95,11 +100,7 @@ export const StakingPanel = ({ className }: { className?: string }) => {
key: STRING_KEYS.UNSTAKED,
})}
- {stakingApr && (
-
- {stringGetter({ key: STRING_KEYS.EST_APR, params: { PERCENTAGE: stakingApr } })}
-
- )}
+ {stakingApr && <$Tag sign={TagSign.Positive}>{aprText}$Tag>}
$Label>
<$BalanceOutput type={OutputType.Asset} value={nativeTokenBalance} />
@@ -107,7 +108,7 @@ export const StakingPanel = ({ className }: { className?: string }) => {
@@ -122,11 +123,7 @@ export const StakingPanel = ({ className }: { className?: string }) => {
key: STRING_KEYS.STAKED,
})}
- {stakingApr && (
-
- {stringGetter({ key: STRING_KEYS.EST_APR, params: { PERCENTAGE: stakingApr } })}
-
- )}
+ {stakingApr && <$Tag>{aprText}$Tag>}
$Label>
<$BalanceOutput type={OutputType.Asset} value={nativeStakingBalance} />
@@ -134,7 +131,7 @@ export const StakingPanel = ({ className }: { className?: string }) => {
@@ -193,6 +190,14 @@ const $Content = styled.div`
gap: 0.75rem;
`;
+const $Tag = styled(Tag)`
+ display: inline-block;
+`;
+
+const $Output = styled(Output)`
+ display: inline-block;
+`;
+
const $TotalBalance = styled(Details)`
div {
--scrollArea-height: auto;
diff --git a/src/pages/token/rewards/StakingRewardPanel.tsx b/src/pages/token/rewards/StakingRewardPanel.tsx
index d340f3d33..0c8924d08 100644
--- a/src/pages/token/rewards/StakingRewardPanel.tsx
+++ b/src/pages/token/rewards/StakingRewardPanel.tsx
@@ -40,10 +40,9 @@ export const StakingRewardPanel = ({ usdcRewards }: ElementProps) => {
const openStakingRewardDialog = useCallback(
() =>
dispatch(
- openDialog({
- type: DialogTypes.StakingReward,
- dialogProps: { validators: validators?.toArray() ?? [], usdcRewards },
- })
+ openDialog(
+ DialogTypes.StakingReward({ validators: validators?.toArray() ?? [], usdcRewards })
+ )
),
[dispatch, validators, usdcRewards]
);
diff --git a/src/pages/token/staking/StakingPanel.tsx b/src/pages/token/staking/StakingPanel.tsx
index 8045affa3..f913b9345 100644
--- a/src/pages/token/staking/StakingPanel.tsx
+++ b/src/pages/token/staking/StakingPanel.tsx
@@ -26,7 +26,7 @@ export const StakingPanel = ({ className }: { className?: string }) => {
<$Img src="/third-party/keplr.png" alt={stringGetter({ key: STRING_KEYS.KEPLR })} />
$Header>
}
- onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
+ onClick={() => dispatch(openDialog(DialogTypes.ExternalNavKeplr()))}
>
<$Description>
{stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
diff --git a/src/pages/token/staking/StrideStakingPanel.tsx b/src/pages/token/staking/StrideStakingPanel.tsx
index 9c4f22ab1..af4db8762 100644
--- a/src/pages/token/staking/StrideStakingPanel.tsx
+++ b/src/pages/token/staking/StrideStakingPanel.tsx
@@ -32,7 +32,7 @@ export const StrideStakingPanel = ({ className }: { className?: string }) => {
<$Img src="/third-party/stride.png" alt="Stride" />
$Header>
}
- onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavStride }))}
+ onClick={() => dispatch(openDialog(DialogTypes.ExternalNavStride()))}
>
<$Description>
{stringGetter({
diff --git a/src/pages/trade/MobileTopPanel.tsx b/src/pages/trade/MobileTopPanel.tsx
index 081757c12..430425b04 100644
--- a/src/pages/trade/MobileTopPanel.tsx
+++ b/src/pages/trade/MobileTopPanel.tsx
@@ -13,11 +13,11 @@ import { Icon, IconName } from '@/components/Icon';
import { Tabs } from '@/components/Tabs';
import { ToggleButton } from '@/components/ToggleButton';
import { AccountInfo } from '@/views/AccountInfo';
+import { CanvasOrderbook } from '@/views/CanvasOrderbook/CanvasOrderbook';
import { DepthChart } from '@/views/charts/DepthChart';
import { FundingChart } from '@/views/charts/FundingChart';
import { TvChart } from '@/views/charts/TvChart';
import { LiveTrades } from '@/views/tables/LiveTrades';
-import { Orderbook } from '@/views/tables/Orderbook';
import { useAppSelector } from '@/state/appTypes';
import { getSelectedLocale } from '@/state/localizationSelectors';
@@ -76,7 +76,7 @@ export const MobileTopPanel = () => {
{
content: (
<$ScrollableTableContainer>
-
+
$ScrollableTableContainer>
),
label: stringGetter({ key: STRING_KEYS.ORDERBOOK_SHORT }),
diff --git a/src/pages/trade/UnopenedIsolatedPositions.tsx b/src/pages/trade/UnopenedIsolatedPositions.tsx
index e2e200224..e94cfc40b 100644
--- a/src/pages/trade/UnopenedIsolatedPositions.tsx
+++ b/src/pages/trade/UnopenedIsolatedPositions.tsx
@@ -1,6 +1,6 @@
-import { ReactNode, useState } from 'react';
+import { ReactNode, useEffect, useState } from 'react';
-import { shallowEqual, useSelector } from 'react-redux';
+import { shallowEqual } from 'react-redux';
import styled from 'styled-components';
import { SubaccountPendingPosition } from '@/constants/abacus';
@@ -15,7 +15,8 @@ import { DropdownIcon } from '@/components/DropdownIcon';
import { IconName } from '@/components/Icon';
import { PotentialPositionCard } from '@/components/PotentialPositionCard';
-import { getNonZeroPendingPositions } from '@/state/accountSelectors';
+import { getExistingOpenPositions, getNonZeroPendingPositions } from '@/state/accountSelectors';
+import { useAppSelector } from '@/state/appTypes';
import { getAssets } from '@/state/assetsSelectors';
type UnopenedIsolatedPositionsProps = {
@@ -27,9 +28,16 @@ export const MaybeUnopenedIsolatedPositionsDrawer = ({
className,
onViewOrders,
}: UnopenedIsolatedPositionsProps) => {
- const [isOpen, setIsOpen] = useState(false);
+ const numNormalPositions = useAppSelector(getExistingOpenPositions, shallowEqual)?.length;
+ const [isOpen, setIsOpen] = useState(numNormalPositions === 0);
+ useEffect(() => {
+ if (numNormalPositions === 0) {
+ setIsOpen(true);
+ }
+ }, [numNormalPositions]);
+
+ const pendingPositions = useAppSelector(getNonZeroPendingPositions, shallowEqual);
- const pendingPositions = useSelector(getNonZeroPendingPositions, shallowEqual);
const stringGetter = useStringGetter();
if (!pendingPositions?.length) return null;
@@ -63,7 +71,7 @@ export const MaybeUnopenedIsolatedPositionsPanel = ({
header,
className,
}: UnopenedIsolatedPositionsPanelProps) => {
- const pendingPositions = useSelector(getNonZeroPendingPositions, shallowEqual);
+ const pendingPositions = useAppSelector(getNonZeroPendingPositions, shallowEqual);
if (!pendingPositions?.length) return null;
return (
@@ -86,7 +94,7 @@ const UnopenedIsolatedPositionsCards = ({
onViewOrders,
pendingPositions,
}: UnopenedIsolatedPositionsCardsProps) => {
- const assetsData = useSelector(getAssets, shallowEqual);
+ const assetsData = useAppSelector(getAssets, shallowEqual);
return (
<$Cards>
{pendingPositions.map((pendingPosition) => (
diff --git a/src/pages/trade/VerticalPanel.tsx b/src/pages/trade/VerticalPanel.tsx
index 1b4a766de..ef74edfc9 100644
--- a/src/pages/trade/VerticalPanel.tsx
+++ b/src/pages/trade/VerticalPanel.tsx
@@ -6,7 +6,7 @@ import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks/useStringGetter';
import { Tabs } from '@/components/Tabs';
-import { CanvasOrderbook } from '@/views/CanvasOrderbook';
+import { CanvasOrderbook } from '@/views/CanvasOrderbook/CanvasOrderbook';
import { LiveTrades } from '@/views/tables/LiveTrades';
enum Tab {
diff --git a/src/pages/trade/types.ts b/src/pages/trade/types.ts
index 4d7897980..37233b087 100644
--- a/src/pages/trade/types.ts
+++ b/src/pages/trade/types.ts
@@ -8,7 +8,7 @@ export function marketTypeMatchesFilter(type: 'Isolated' | 'Cross', filter?: Mar
return (
filter == null ||
filter === MarketTypeFilter.AllMarkets ||
- (type === 'Isolated' && filter === MarketTypeFilter.Cross) ||
- (type === 'Cross' && filter === MarketTypeFilter.Isolated)
+ (type === 'Isolated' && filter === MarketTypeFilter.Isolated) ||
+ (type === 'Cross' && filter === MarketTypeFilter.Cross)
);
}
diff --git a/src/state/accountSelectors.ts b/src/state/accountSelectors.ts
index e179873c7..ca987b92e 100644
--- a/src/state/accountSelectors.ts
+++ b/src/state/accountSelectors.ts
@@ -27,7 +27,11 @@ import { getHydratedPositionData } from '@/lib/positions';
import { type RootState } from './_store';
import { createAppSelector } from './appTypes';
import { getAssets } from './assetsSelectors';
-import { getCurrentMarketId, getPerpetualMarkets } from './perpetualsSelectors';
+import {
+ getCurrentMarketId,
+ getCurrentMarketOrderbook,
+ getPerpetualMarkets,
+} from './perpetualsSelectors';
/**
* @param state
@@ -331,22 +335,34 @@ export const getSubaccountConditionalOrders = () =>
* @param state
* @returns list of orders that are in the open status
*/
-export const getSubaccountOpenStatusOrders = createAppSelector([getSubaccountOrders], (orders) =>
- orders?.filter((order) => order.status === AbacusOrderStatus.Open)
+export const getSubaccountOpenOrdersForCurrentMarket = createAppSelector(
+ [getSubaccountOrders, getCurrentMarketId],
+ (orders, marketId) =>
+ orders?.filter(
+ (order) =>
+ order.status === AbacusOrderStatus.Open && marketId != null && order.marketId === marketId
+ )
);
-export const getSubaccountOrderSizeBySideAndPrice = createAppSelector(
- [getSubaccountOpenStatusOrders],
- (openOrders = []) => {
+export const getSubaccountOrderSizeBySideAndOrderbookLevel = createAppSelector(
+ [getSubaccountOpenOrdersForCurrentMarket, getCurrentMarketOrderbook],
+ (openOrders = [], book = undefined) => {
+ const tickSize = book?.grouping?.tickSize;
const orderSizeBySideAndPrice: Partial
>> = {};
openOrders.forEach((order: SubaccountOrder) => {
const side = ORDER_SIDES[order.side.name];
const byPrice = (orderSizeBySideAndPrice[side] ??= {});
- if (byPrice[order.price]) {
- byPrice[order.price] += order.size;
- } else {
- byPrice[order.price] = order.size;
- }
+
+ const priceOrderbookLevel = (() => {
+ if (tickSize == null) {
+ return order.price;
+ }
+ const tickLevelUnrounded = order.price / tickSize;
+ const tickLevel =
+ side === OrderSide.BUY ? Math.floor(tickLevelUnrounded) : Math.ceil(tickLevelUnrounded);
+ return tickLevel * tickSize;
+ })();
+ byPrice[priceOrderbookLevel] = (byPrice[priceOrderbookLevel] ?? 0) + order.size;
});
return orderSizeBySideAndPrice;
}
@@ -639,7 +655,19 @@ export const getTotalTradingRewards = (state: RootState) => state.account?.tradi
* @returns account trading rewards aggregated by period
*/
export const getHistoricalTradingRewards = (state: RootState) =>
- state.account?.tradingRewards?.historical;
+ state.account?.tradingRewards?.filledHistory;
+
+/**
+ * @returns account historical trading rewards for the specified perid
+ */
+export const getTradingRewardsEventsForPeriod = () =>
+ createAppSelector(
+ [
+ (state: RootState) => state.account?.tradingRewards?.rawHistory,
+ (s, period: string) => period,
+ ],
+ (historicalTradingRewards, period) => historicalTradingRewards?.get(period)?.toArray()
+ );
/**
* @returns account historical trading rewards for the specified perid
@@ -647,7 +675,7 @@ export const getHistoricalTradingRewards = (state: RootState) =>
export const getHistoricalTradingRewardsForPeriod = () =>
createAppSelector(
[getHistoricalTradingRewards, (s, period: string) => period],
- (historicalTradingRewards, period) => historicalTradingRewards?.get(period)
+ (historicalTradingRewards, period) => historicalTradingRewards?.get(period)?.toArray()
);
const historicalRewardsForCurrentWeekSelector = getHistoricalTradingRewardsForPeriod();
@@ -656,7 +684,7 @@ const historicalRewardsForCurrentWeekSelector = getHistoricalTradingRewardsForPe
*/
export const getHistoricalTradingRewardsForCurrentWeek = createAppSelector(
[(s) => historicalRewardsForCurrentWeekSelector(s, HistoricalTradingRewardsPeriod.WEEKLY.name)],
- (historicalTradingRewards) => historicalTradingRewards?.firstOrNull()
+ (historicalTradingRewards) => historicalTradingRewards?.[0]
);
/**
diff --git a/src/state/dialogs.ts b/src/state/dialogs.ts
index ee4c0b891..ad6fc6319 100644
--- a/src/state/dialogs.ts
+++ b/src/state/dialogs.ts
@@ -1,16 +1,11 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
-import type { DialogTypes, TradeBoxDialogTypes } from '@/constants/dialogs';
-
-type DialogInfo = {
- type: TDialog;
- dialogProps?: any;
-};
+import type { DialogType, TradeBoxDialogType } from '@/constants/dialogs';
export interface DialogsState {
- activeDialog?: DialogInfo;
- activeTradeBoxDialog?: DialogInfo;
- dialogQueue: DialogInfo[];
+ activeDialog?: DialogType;
+ activeTradeBoxDialog?: TradeBoxDialogType;
+ dialogQueue: DialogType[];
}
const initialState: DialogsState = {
@@ -23,7 +18,7 @@ export const dialogsSlice = createSlice({
name: 'Dialogs',
initialState,
reducers: {
- openDialog: (state: DialogsState, action: PayloadAction>) => {
+ openDialog: (state, action: PayloadAction) => {
if (state.activeDialog?.type === action.payload.type) return;
if (state.activeDialog) {
@@ -32,22 +27,19 @@ export const dialogsSlice = createSlice({
state.activeDialog = action.payload;
}
},
- closeDialog: (state: DialogsState) => {
+ closeDialog: (state) => {
state.activeDialog = state.dialogQueue.shift();
},
- forceOpenDialog: (state: DialogsState, action: PayloadAction>) => {
+ forceOpenDialog: (state, action: PayloadAction) => {
if (state.activeDialog) {
state.dialogQueue.unshift(state.activeDialog);
}
state.activeDialog = action.payload;
},
- closeDialogInTradeBox: (state: DialogsState) => {
+ closeDialogInTradeBox: (state) => {
state.activeTradeBoxDialog = undefined;
},
- openDialogInTradeBox: (
- state: DialogsState,
- action: PayloadAction>
- ) => {
+ openDialogInTradeBox: (state, action: PayloadAction) => {
state.activeTradeBoxDialog = action.payload;
},
},
diff --git a/src/views/AccountInfo/AccountInfoConnectedState.tsx b/src/views/AccountInfo/AccountInfoConnectedState.tsx
index 52f4c80bf..4c0b18ead 100644
--- a/src/views/AccountInfo/AccountInfoConnectedState.tsx
+++ b/src/views/AccountInfo/AccountInfoConnectedState.tsx
@@ -84,7 +84,7 @@ export const AccountInfoConnectedState = () => {
<$TransferButtons>
<$Button
state={{ isDisabled: !dydxAccounts }}
- onClick={() => dispatch(openDialog({ type: DialogTypes.Withdraw }))}
+ onClick={() => dispatch(openDialog(DialogTypes.Withdraw()))}
shape={ButtonShape.Rectangle}
size={ButtonSize.XSmall}
>
@@ -94,7 +94,7 @@ export const AccountInfoConnectedState = () => {
<>
<$Button
state={{ isDisabled: !dydxAccounts }}
- onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
+ onClick={() => dispatch(openDialog(DialogTypes.Deposit()))}
shape={ButtonShape.Rectangle}
size={ButtonSize.XSmall}
>
@@ -106,10 +106,7 @@ export const AccountInfoConnectedState = () => {
iconName={IconName.Send}
onClick={() =>
dispatch(
- openDialog({
- type: DialogTypes.Transfer,
- dialogProps: { selectedAsset: DydxChainAsset.USDC },
- })
+ openDialog(DialogTypes.Transfer({ selectedAsset: DydxChainAsset.USDC }))
)
}
/>
@@ -123,7 +120,7 @@ export const AccountInfoConnectedState = () => {
{!showHeader && !isTablet && complianceState === ComplianceStates.FULL_ACCESS && (
<$CornerButton
state={{ isDisabled: !dydxAccounts }}
- onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
+ onClick={() => dispatch(openDialog(DialogTypes.Deposit()))}
>
<$CircleContainer>
diff --git a/src/views/CanvasOrderbook/index.tsx b/src/views/CanvasOrderbook/CanvasOrderbook.tsx
similarity index 51%
rename from src/views/CanvasOrderbook/index.tsx
rename to src/views/CanvasOrderbook/CanvasOrderbook.tsx
index 61187a557..457797f23 100644
--- a/src/views/CanvasOrderbook/index.tsx
+++ b/src/views/CanvasOrderbook/CanvasOrderbook.tsx
@@ -1,4 +1,4 @@
-import { forwardRef, useCallback, useMemo, useRef } from 'react';
+import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
import { shallowEqual } from 'react-redux';
import styled, { css } from 'styled-components';
@@ -6,7 +6,7 @@ import styled, { css } from 'styled-components';
import { Nullable, type PerpetualMarketOrderbookLevel } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { SMALL_USD_DECIMALS, USD_DECIMALS } from '@/constants/numbers';
-import { ORDERBOOK_HEIGHT, ORDERBOOK_MAX_ROWS_PER_SIDE } from '@/constants/orderbook';
+import { ORDERBOOK_MAX_ROWS_PER_SIDE, ORDERBOOK_ROW_HEIGHT } from '@/constants/orderbook';
import { useCenterOrderbook } from '@/hooks/Orderbook/useCenterOrderbook';
import { useDrawOrderbook } from '@/hooks/Orderbook/useDrawOrderbook';
@@ -19,39 +19,49 @@ import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { Tag } from '@/components/Tag';
import { useAppDispatch, useAppSelector } from '@/state/appTypes';
-import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { setTradeFormInputs } from '@/state/inputs';
import { getCurrentInput } from '@/state/inputsSelectors';
-import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors';
+import {
+ getCurrentMarketConfig,
+ getCurrentMarketData,
+ getCurrentMarketId,
+} from '@/state/perpetualsSelectors';
import { MustBigNumber } from '@/lib/numbers';
+import { OrderbookControls } from './OrderbookControls';
import { OrderbookMiddleRow, OrderbookRow } from './OrderbookRow';
type ElementProps = {
maxRowsPerSide?: number;
+ layout?: 'vertical' | 'horizontal';
};
type StyleProps = {
histogramSide?: 'left' | 'right';
+ hideHeader?: boolean;
};
export const CanvasOrderbook = forwardRef(
(
{
histogramSide = 'right',
+ hideHeader = false,
+ layout = 'vertical',
maxRowsPerSide = ORDERBOOK_MAX_ROWS_PER_SIDE,
}: ElementProps & StyleProps,
ref: React.ForwardedRef
) => {
- const { asks, bids, hasOrderbook, histogramRange } = useCalculateOrderbookData({
- maxRowsPerSide,
- });
+ const { asks, bids, hasOrderbook, histogramRange, currentGrouping } = useCalculateOrderbookData(
+ {
+ maxRowsPerSide,
+ }
+ );
const stringGetter = useStringGetter();
const currentMarket = useAppSelector(getCurrentMarketId) ?? '';
const currentMarketConfig = useAppSelector(getCurrentMarketConfig, shallowEqual);
- const { id = '' } = useAppSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
+ const { assetId: id } = useAppSelector(getCurrentMarketData, shallowEqual) ?? {};
const { tickSizeDecimals = USD_DECIMALS } = currentMarketConfig ?? {};
@@ -59,28 +69,37 @@ export const CanvasOrderbook = forwardRef(
* Slice asks and bids to maxRowsPerSide using empty rows
*/
const { asksSlice, bidsSlice } = useMemo(() => {
- let newAsksSlice: Array = [];
const emptyAskRows =
asks.length < maxRowsPerSide
? new Array(maxRowsPerSide - asks.length).fill(undefined)
: [];
- newAsksSlice = [...emptyAskRows, ...asks.reverse()];
- let newBidsSlice: Array = [];
+ const newAsksSlice: Array = [
+ ...emptyAskRows,
+ ...asks.reverse(),
+ ];
+
const emptyBidRows =
bids.length < maxRowsPerSide
? new Array(maxRowsPerSide - bids.length).fill(undefined)
: [];
- newBidsSlice = [...bids, ...emptyBidRows];
+ const newBidsSlice: Array = [
+ ...bids,
+ ...emptyBidRows,
+ ];
return {
- asksSlice: newAsksSlice,
+ asksSlice: layout === 'horizontal' ? newAsksSlice : newAsksSlice.reverse(),
bidsSlice: newBidsSlice,
};
- }, [asks, bids]);
+ }, [asks, bids, layout, maxRowsPerSide]);
const orderbookRef = useRef(null);
- useCenterOrderbook({ orderbookRef, marketId: currentMarket });
+ useCenterOrderbook({
+ orderbookRef,
+ marketId: currentMarket,
+ disabled: layout === 'horizontal',
+ });
/**
* Display top or bottom middleRow when center middleRow is off screen
@@ -111,88 +130,127 @@ export const CanvasOrderbook = forwardRef(
[currentInput, tickSizeDecimals]
);
+ const [displayUnit, setDisplayUnit] = useState<'fiat' | 'asset'>('asset');
+
const { canvasRef: asksCanvasRef } = useDrawOrderbook({
- data: [...asksSlice].reverse(),
+ data: asksSlice,
histogramRange,
histogramSide,
+ displayUnit,
side: 'ask',
});
const { canvasRef: bidsCanvasRef } = useDrawOrderbook({
data: bidsSlice,
histogramRange,
- histogramSide,
+ histogramSide: layout === 'horizontal' ? 'left' : histogramSide,
+ displayUnit,
side: 'bid',
});
+ const usdTag = USD;
+ const assetTag = id ? {id} : undefined;
+ const asksOrderbook = (
+ <$OrderbookSideContainer $side="asks" $rows={maxRowsPerSide}>
+ <$HoverRows $bottom={layout !== 'horizontal'}>
+ {[...asksSlice].reverse().map((row: PerpetualMarketOrderbookLevel | undefined, idx) =>
+ row ? (
+ <$Row
+ // eslint-disable-next-line react/no-array-index-key
+ key={idx}
+ title={`${row.price}`}
+ onClick={() => {
+ onRowAction(row.price);
+ }}
+ />
+ ) : (
+ // eslint-disable-next-line react/no-array-index-key
+ <$Row key={idx} />
+ )
+ )}
+ $HoverRows>
+ <$OrderbookCanvas ref={asksCanvasRef} width="100%" height="100%" />
+ $OrderbookSideContainer>
+ );
+ const bidsOrderbook = (
+ <$OrderbookSideContainer $side="bids" $rows={maxRowsPerSide}>
+ <$HoverRows>
+ {bidsSlice.map((row: PerpetualMarketOrderbookLevel | undefined, idx) =>
+ row ? (
+ <$Row
+ // eslint-disable-next-line react/no-array-index-key
+ key={idx}
+ title={`${row.price}`}
+ onClick={
+ row?.price
+ ? () => {
+ onRowAction(row.price);
+ }
+ : undefined
+ }
+ />
+ ) : (
+ // eslint-disable-next-line react/no-array-index-key
+ <$Row key={idx} />
+ )
+ )}
+ $HoverRows>
+ <$OrderbookCanvas ref={bidsCanvasRef} width="100%" height="100%" />
+ $OrderbookSideContainer>
+ );
return (
<$OrderbookContainer ref={ref}>
<$OrderbookContent $isLoading={!hasOrderbook}>
- <$Header>
-
- {stringGetter({ key: STRING_KEYS.SIZE })} {id && {id}}
-
-
- {stringGetter({ key: STRING_KEYS.PRICE })} USD
-
- {stringGetter({ key: STRING_KEYS.MINE })}
- $Header>
-
- {displaySide === 'top' && (
- <$OrderbookMiddleRow side="top" tickSizeDecimals={tickSizeDecimals} />
+ {!hideHeader && (
+
+ )}
+ {!hideHeader && (
+ <$Header>
+
+ {stringGetter({ key: STRING_KEYS.PRICE })} {usdTag}
+
+
+ {stringGetter({ key: STRING_KEYS.SIZE })}{' '}
+ {displayUnit === 'fiat' ? usdTag : assetTag}
+
+
+ {stringGetter({ key: STRING_KEYS.TOTAL })}{' '}
+ {displayUnit === 'fiat' ? usdTag : assetTag}
+
+ $Header>
)}
- <$OrderbookWrapper ref={orderbookRef}>
- <$OrderbookSideContainer $side="asks">
- <$HoverRows $bottom>
- {asksSlice.map((row: PerpetualMarketOrderbookLevel | undefined, idx) =>
- row ? (
- <$Row
- // eslint-disable-next-line react/no-array-index-key
- key={idx}
- title={`${row.price}`}
- onClick={() => {
- onRowAction(row.price);
- }}
- />
- ) : (
- // eslint-disable-next-line react/no-array-index-key
- <$Row key={idx} />
- )
- )}
- $HoverRows>
- <$OrderbookCanvas ref={asksCanvasRef} width="100%" height="100%" />
- $OrderbookSideContainer>
-
-
-
- <$OrderbookSideContainer $side="bids">
- <$HoverRows>
- {bidsSlice.map((row: PerpetualMarketOrderbookLevel | undefined, idx) =>
- row ? (
- <$Row
- // eslint-disable-next-line react/no-array-index-key
- key={idx}
- title={`${row.price}`}
- onClick={
- row?.price
- ? () => {
- onRowAction(row.price);
- }
- : undefined
- }
- />
- ) : (
- // eslint-disable-next-line react/no-array-index-key
- <$Row key={idx} />
- )
- )}
- $HoverRows>
- <$OrderbookCanvas ref={bidsCanvasRef} width="100%" height="100%" />
- $OrderbookSideContainer>
- $OrderbookWrapper>
- {displaySide === 'bottom' && (
- <$OrderbookMiddleRow side="bottom" tickSizeDecimals={tickSizeDecimals} />
+ {(displaySide === 'top' || layout === 'horizontal') && (
+ <$OrderbookMiddleRow
+ side="top"
+ tickSizeDecimals={tickSizeDecimals}
+ isHeader={layout === 'horizontal'}
+ />
+ )}
+ {layout === 'vertical' ? (
+ <>
+ <$OrderbookWrapper ref={orderbookRef}>
+ {asksOrderbook}
+
+ {bidsOrderbook}
+ $OrderbookWrapper>
+ {displaySide === 'bottom' && (
+ <$OrderbookMiddleRow side="bottom" tickSizeDecimals={tickSizeDecimals} />
+ )}
+ >
+ ) : (
+ <$HorizontalOrderbook>
+ {asksOrderbook}
+ {bidsOrderbook}
+ $HorizontalOrderbook>
)}
$OrderbookContent>
{!hasOrderbook && }
@@ -200,6 +258,7 @@ export const CanvasOrderbook = forwardRef(
);
}
);
+
const $OrderbookContainer = styled.div`
display: flex;
flex: 1 1 0%;
@@ -228,8 +287,16 @@ const $OrderbookWrapper = styled.div`
flex: 1 1 0%;
`;
-const $OrderbookSideContainer = styled.div<{ $side: 'bids' | 'asks' }>`
- min-height: ${ORDERBOOK_HEIGHT}px;
+const $HorizontalOrderbook = styled.div`
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ overflow-y: auto;
+`;
+
+const $OrderbookSideContainer = styled.div<{ $side: 'bids' | 'asks'; $rows: number }>`
+ ${({ $rows }) => css`
+ min-height: calc(${$rows} * ${ORDERBOOK_ROW_HEIGHT}px);
+ `}
${({ $side }) => css`
--accent-color: ${$side === 'bids' ? 'var(--color-positive)' : 'var(--color-negative)'};
`}
diff --git a/src/views/CanvasOrderbook/OrderbookControls.tsx b/src/views/CanvasOrderbook/OrderbookControls.tsx
new file mode 100644
index 000000000..326903c80
--- /dev/null
+++ b/src/views/CanvasOrderbook/OrderbookControls.tsx
@@ -0,0 +1,143 @@
+import { useCallback } from 'react';
+
+import { clamp } from 'lodash';
+import { shallowEqual } from 'react-redux';
+import styled from 'styled-components';
+
+import { MarketOrderbookGrouping, Nullable, OrderbookGrouping } from '@/constants/abacus';
+import { ButtonShape, ButtonSize } from '@/constants/buttons';
+import { STRING_KEYS } from '@/constants/localization';
+import { USD_DECIMALS } from '@/constants/numbers';
+
+import { useStringGetter } from '@/hooks/useStringGetter';
+
+import { Button } from '@/components/Button';
+import { Output, OutputType } from '@/components/Output';
+import { ToggleGroup } from '@/components/ToggleGroup';
+
+import { useAppSelector } from '@/state/appTypes';
+import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
+
+import abacusStateManager from '@/lib/abacus';
+
+type OrderbookControlsProps = {
+ className?: string;
+ assetName?: string;
+ selectedUnit: 'fiat' | 'asset';
+ setSelectedUnit(val: 'fiat' | 'asset'): void;
+ grouping: Nullable;
+};
+
+export const OrderbookControls = ({
+ className,
+ assetName,
+ selectedUnit,
+ setSelectedUnit,
+ grouping,
+}: OrderbookControlsProps) => {
+ const stringGetter = useStringGetter();
+ const modifyScale = useCallback(
+ (direction: number) => {
+ const start = grouping?.multiplier.ordinal ?? 0;
+ const end = clamp(start + direction, 0, 3);
+ abacusStateManager.modifyOrderbookLevel(
+ OrderbookGrouping.values().find((v) => v.ordinal === end)!
+ );
+ },
+ [grouping?.multiplier.ordinal]
+ );
+ const currentMarketConfig = useAppSelector(getCurrentMarketConfig, shallowEqual);
+ const tickSizeDecimals = currentMarketConfig?.tickSizeDecimals ?? USD_DECIMALS;
+
+ return (
+ <$OrderbookControlsContainer className={className}>
+ <$OrderbookUnitControl>
+ <$OrderbookLabel>{stringGetter({ key: STRING_KEYS.ORDERBOOK_UNITS })}$OrderbookLabel>
+
+ $OrderbookUnitControl>
+ <$OrderbookZoomControl>
+ <$OrderbookLabel>{stringGetter({ key: STRING_KEYS.ORDERBOOK_GROUPING })}$OrderbookLabel>
+ <$ZoomControls>
+
+ <$ButtonGroup>
+
+
+ $ButtonGroup>
+ $ZoomControls>
+ $OrderbookZoomControl>
+ $OrderbookControlsContainer>
+ );
+};
+
+const $OrderbookLabel = styled.div`
+ display: inline-flex;
+ align-items: center;
+`;
+
+const $OrderbookControlsContainer = styled.div`
+ color: var(--color-text-0);
+ font: var(--font-small-book);
+
+ display: flex;
+ flex-direction: column;
+
+ > * {
+ padding: 0.5rem;
+ padding-top: 0.3rem;
+ padding-bottom: 0.3rem;
+ }
+`;
+const $ZoomControls = styled.div`
+ display: flex;
+ gap: 0.5rem;
+`;
+
+const $ButtonGroup = styled.div`
+ display: flex;
+ gap: 0.25rem;
+ > button {
+ --button-font: var(--font-medium-book);
+ }
+`;
+
+const $OrderbookUnitControl = styled.div`
+ display: flex;
+ justify-content: space-between;
+ gap: 0.5rem;
+ border-bottom: var(--border);
+`;
+
+const $OrderbookZoomControl = styled.div`
+ gap: 1rem;
+ display: flex;
+ justify-content: space-between;
+ box-shadow: 0 0 0 var(--border-width) var(--border-color);
+`;
+
+const $MinusSymbolCenter = styled.span`
+ margin-top: -0.125rem;
+`;
diff --git a/src/views/CanvasOrderbook/OrderbookRow.tsx b/src/views/CanvasOrderbook/OrderbookRow.tsx
index 3651b2d52..909d8e107 100644
--- a/src/views/CanvasOrderbook/OrderbookRow.tsx
+++ b/src/views/CanvasOrderbook/OrderbookRow.tsx
@@ -16,13 +16,14 @@ import { getCurrentMarketMidMarketPriceWithOraclePriceFallback } from '@/state/p
type StyleProps = {
side?: 'top' | 'bottom';
+ isHeader?: boolean;
};
type ElementProps = {
tickSizeDecimals?: Nullable;
};
-export const OrderbookRow = styled.div`
+export const OrderbookRow = styled.div<{ isHeader?: boolean }>`
display: flex;
flex-shrink: 0;
align-items: center;
@@ -36,24 +37,34 @@ export const OrderbookRow = styled.div`
flex: 1 1 0%;
text-align: right;
}
+
+ ${({ isHeader }) =>
+ isHeader
+ ? `
+ padding-left: 2rem;
+ gap: 2rem;
+ > span {
+ flex: 0 0 0%;
+ }
+ `
+ : ``}
`;
export const OrderbookMiddleRow = forwardRef(
- ({ side, tickSizeDecimals = TOKEN_DECIMALS }, ref) => {
+ ({ side, isHeader, tickSizeDecimals = TOKEN_DECIMALS }, ref) => {
const stringGetter = useStringGetter();
const orderbookMidMarketPrice = useAppSelector(
getCurrentMarketMidMarketPriceWithOraclePriceFallback
);
return (
- <$OrderbookMiddleRow ref={ref} side={side}>
+ <$OrderbookMiddleRow ref={ref} side={side} isHeader={isHeader}>
{stringGetter({ key: STRING_KEYS.PRICE })}
<$PriceOutputSpan>
<$Output
type={OutputType.Number}
value={orderbookMidMarketPrice}
fractionDigits={tickSizeDecimals}
- useGrouping={false}
/>
$PriceOutputSpan>
{/* Empty cell */}
diff --git a/src/views/ExchangeBillboards.tsx b/src/views/ExchangeBillboards.tsx
index 05cc5ff4b..7fc0d8456 100644
--- a/src/views/ExchangeBillboards.tsx
+++ b/src/views/ExchangeBillboards.tsx
@@ -93,7 +93,7 @@ export const ExchangeBillboards: React.FC = () => {
const $MarketBillboardsWrapper = styled.div`
${layoutMixins.column}
- gap: 1rem;
+ gap: 0.5rem;
`;
const $BillboardContainer = styled.div`
${layoutMixins.row}
@@ -101,7 +101,7 @@ const $BillboardContainer = styled.div`
justify-content: space-between;
background-color: var(--color-layer-3);
- padding: 0.5rem 1.5rem;
+ padding: 1rem 1.5rem;
border-radius: 0.625rem;
`;
const $BillboardLink = styled(Button)`
diff --git a/src/views/MarketsStats.tsx b/src/views/MarketsStats.tsx
index d9da5b782..42ce6e161 100644
--- a/src/views/MarketsStats.tsx
+++ b/src/views/MarketsStats.tsx
@@ -95,7 +95,10 @@ const $NewTag = styled(Tag)`
`;
const $ToggleGroupContainer = styled.div`
${layoutMixins.row}
- margin-left: auto;
+ position: absolute;
+ top: 0.8125rem;
+ right: 1rem;
+ z-index: 2;
& button {
--button-toggle-off-backgroundColor: var(--color-layer-3);
@@ -108,11 +111,10 @@ const $ToggleGroupContainer = styled.div`
`;
const $SectionHeader = styled.div`
${layoutMixins.row}
+ position: relative;
- justify-content: space-between;
- padding: 0.5rem 1.5rem;
- gap: 0.375rem;
- height: 2.5rem;
+ padding: 1rem 1.5rem 0;
+ gap: 0.25rem;
& h4 {
font: var(--font-base-medium);
diff --git a/src/views/MobileDownloadLinks.tsx b/src/views/MobileDownloadLinks.tsx
new file mode 100644
index 000000000..68870b634
--- /dev/null
+++ b/src/views/MobileDownloadLinks.tsx
@@ -0,0 +1,102 @@
+import styled from 'styled-components';
+
+import { ButtonShape, ButtonType } from '@/constants/buttons';
+import { STRING_KEYS } from '@/constants/localization';
+
+import { useMobileAppUrl } from '@/hooks/useMobileAppUrl';
+import { useStringGetter } from '@/hooks/useStringGetter';
+
+import { IconName } from '@/components/Icon';
+import { IconButton } from '@/components/IconButton';
+import { Link } from '@/components/Link';
+import { VerticalSeparator } from '@/components/Separator';
+
+export const MobileDownloadLinks = ({ withBadges }: { withBadges?: boolean }) => {
+ const stringGetter = useStringGetter();
+ const { appleAppStoreUrl, googlePlayStoreUrl } = useMobileAppUrl();
+
+ if (!appleAppStoreUrl && !googlePlayStoreUrl) return null;
+
+ if (withBadges) {
+ return (
+ <$DownloadLinksInDropdown>
+ <$Download>{stringGetter({ key: STRING_KEYS.GET_DYDX_ON_PHONE })}$Download>
+ {googlePlayStoreUrl && (
+
+
+
+ )}
+ {appleAppStoreUrl && (
+
+
+
+ )}
+ $DownloadLinksInDropdown>
+ );
+ }
+
+ return (
+ <>
+ <$DownloadLinks>
+ <$Download>{stringGetter({ key: STRING_KEYS.DOWNLOAD })}$Download>
+ {googlePlayStoreUrl && (
+ <$AppLink
+ type={ButtonType.Link}
+ href={googlePlayStoreUrl}
+ shape={ButtonShape.Rectangle}
+ iconName={IconName.GooglePlay}
+ />
+ )}
+ {appleAppStoreUrl && (
+ <$AppLink
+ type={ButtonType.Link}
+ href={appleAppStoreUrl}
+ shape={ButtonShape.Rectangle}
+ iconName={IconName.Apple}
+ />
+ )}
+ $DownloadLinks>
+
+
+ >
+ );
+};
+
+const $DownloadLinksInDropdown = styled.div`
+ border-top: solid var(--border-width) var(--color-border);
+ display: grid;
+ grid-template:
+ 'label label' auto
+ 'android ios' 1fr
+ / 1fr 1fr;
+
+ padding: 0.75rem 0.75rem 0;
+ gap: 0.5rem;
+
+ img {
+ width: 7.5rem;
+ }
+`;
+
+const $DownloadLinks = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 0.5rem;
+`;
+
+const $Download = styled.span`
+ grid-area: label;
+ font: var(--font-small-medium);
+ color: var(--color-text-0);
+`;
+
+const $AppLink = styled(IconButton)`
+ --button-icon-size: 1rem;
+ --button-padding: 0 0.5em;
+
+ // apple logo is white.
+ svg {
+ fill: var(--color-white);
+ }
+`;
diff --git a/src/views/PositionInfo.tsx b/src/views/PositionInfo.tsx
index f3986ab4b..6d6b6e158 100644
--- a/src/views/PositionInfo.tsx
+++ b/src/views/PositionInfo.tsx
@@ -69,7 +69,6 @@ export const PositionInfo = ({ showNarrowVariation }: { showNarrowVariation?: bo
const { stepSizeDecimals, tickSizeDecimals } = currentMarketConfigs ?? {};
const { id } = currentMarketAssetData ?? {};
- const { type: tradeBoxDialogType } = activeTradeBoxDialog ?? {};
const {
adjustedImf,
@@ -287,18 +286,19 @@ export const PositionInfo = ({ showNarrowVariation }: { showNarrowVariation?: bo
const actions = (
<$Actions>
{isTablet ? (
- <$ClosePositionButton
- onClick={() => dispatch(openDialog({ type: DialogTypes.ClosePosition }))}
- >
+ <$ClosePositionButton onClick={() => dispatch(openDialog(DialogTypes.ClosePosition()))}>
{stringGetter({ key: STRING_KEYS.CLOSE_POSITION })}
$ClosePositionButton>
) : (
<$ClosePositionToggleButton
- isPressed={tradeBoxDialogType === TradeBoxDialogTypes.ClosePosition}
+ isPressed={
+ activeTradeBoxDialog != null &&
+ TradeBoxDialogTypes.is.ClosePosition(activeTradeBoxDialog)
+ }
onPressedChange={(isPressed: boolean) => {
dispatch(
isPressed
- ? openDialogInTradeBox({ type: TradeBoxDialogTypes.ClosePosition })
+ ? openDialogInTradeBox(TradeBoxDialogTypes.ClosePosition())
: closeDialogInTradeBox()
);
diff --git a/src/views/StakeRewardButtonAndReceipt.tsx b/src/views/StakeRewardButtonAndReceipt.tsx
index a2d1aae57..90d654f7b 100644
--- a/src/views/StakeRewardButtonAndReceipt.tsx
+++ b/src/views/StakeRewardButtonAndReceipt.tsx
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from 'react';
import { SelectedGasDenom } from '@dydxprotocol/v4-client-js';
-import { shallowEqual, useDispatch } from 'react-redux';
+import { shallowEqual } from 'react-redux';
import styled, { css } from 'styled-components';
import { AlertType } from '@/constants/alerts';
@@ -16,7 +16,7 @@ import { useTokenConfigs } from '@/hooks/useTokenConfigs';
import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton';
import { calculateCanAccountTrade } from '@/state/accountCalculators';
-import { useAppSelector } from '@/state/appTypes';
+import { useAppDispatch, useAppSelector } from '@/state/appTypes';
import { forceOpenDialog } from '@/state/dialogs';
import { BigNumberish, MustBigNumber } from '@/lib/numbers';
@@ -70,7 +70,7 @@ export const StakeRewardButtonAndReceipt = ({
onClick,
className,
}: ElementProps & StyleProps) => {
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const stringGetter = useStringGetter();
const canAccountTrade = useAppSelector(calculateCanAccountTrade, shallowEqual);
@@ -79,7 +79,7 @@ export const StakeRewardButtonAndReceipt = ({
const [errorToDisplay, setErrorToDisplay] = useState(alert);
const depositFunds = useCallback(
- () => dispatch(forceOpenDialog({ type: DialogTypes.Deposit })),
+ () => dispatch(forceOpenDialog(DialogTypes.Deposit())),
[dispatch]
);
diff --git a/src/views/TradeBox.tsx b/src/views/TradeBox.tsx
index 6097082fc..29ad6c519 100644
--- a/src/views/TradeBox.tsx
+++ b/src/views/TradeBox.tsx
@@ -27,23 +27,26 @@ export const TradeBox = () => {
const activeDialogConfig =
activeDialog &&
- {
- [TradeBoxDialogTypes.ClosePosition]: {
- title: stringGetter({ key: STRING_KEYS.CLOSE_POSITION }),
- content: (
- dispatch(closeDialogInTradeBox())} />
- ),
- onClose: () => {
- abacusStateManager.clearClosePositionInputValues({ shouldFocusOnTradeInput: true });
- },
- },
- [TradeBoxDialogTypes.SelectMarginMode]: {
- title: stringGetter({ key: STRING_KEYS.MARGIN_MODE }),
- content: (
- dispatch(closeDialogInTradeBox())} />
- ),
- },
- }[activeDialog.type];
+ TradeBoxDialogTypes.match<{ title: string; content: JSX.Element; onClose?(): void }>(
+ activeDialog,
+ {
+ SelectMarginMode: () => ({
+ title: stringGetter({ key: STRING_KEYS.MARGIN_MODE }),
+ content: (
+ dispatch(closeDialogInTradeBox())} />
+ ),
+ }),
+ ClosePosition: () => ({
+ title: stringGetter({ key: STRING_KEYS.CLOSE_POSITION }),
+ content: (
+ dispatch(closeDialogInTradeBox())} />
+ ),
+ onClose: () => {
+ abacusStateManager.clearClosePositionInputValues({ shouldFocusOnTradeInput: true });
+ },
+ }),
+ }
+ );
return (
<$TradeBox>
@@ -60,7 +63,6 @@ export const TradeBox = () => {
if (!isOpen) activeDialogConfig?.onClose?.();
}}
placement={DialogPlacement.Inline}
- {...activeDialog?.dialogProps}
>
{activeDialogConfig?.content}
$Dialog>
diff --git a/src/views/charts/DepthChart/Tooltip.tsx b/src/views/charts/DepthChart/Tooltip.tsx
index 37fa80c75..6c83960a6 100644
--- a/src/views/charts/DepthChart/Tooltip.tsx
+++ b/src/views/charts/DepthChart/Tooltip.tsx
@@ -203,11 +203,7 @@ export const DepthChartTooltipContent = ({
),
},
diff --git a/src/views/charts/TradingRewardsChart.tsx b/src/views/charts/TradingRewardsChart.tsx
index fcb8e8dd5..22dd3ccc5 100644
--- a/src/views/charts/TradingRewardsChart.tsx
+++ b/src/views/charts/TradingRewardsChart.tsx
@@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import { kollections } from '@dydxprotocol/v4-abacus';
import { curveLinear } from '@visx/curve';
import { TooltipContextType } from '@visx/xychart';
import { debounce } from 'lodash';
@@ -87,21 +86,20 @@ export const TradingRewardsChart = ({
const canViewAccount = useAppSelector(calculateCanViewAccount);
const totalTradingRewards = useAppSelector(getTotalTradingRewards);
- const periodTradingRewards: Nullable> =
- useParameterizedSelector(getHistoricalTradingRewardsForPeriod, SELECTED_PERIOD.name);
+ const periodTradingRewards: Nullable> = useParameterizedSelector(
+ getHistoricalTradingRewardsForPeriod,
+ SELECTED_PERIOD.name
+ );
const rewardsData = useMemo(
() =>
periodTradingRewards && canViewAccount
- ? periodTradingRewards
- .toArray()
- .reverse()
- .map(
- (datum): TradingRewardsDatum => ({
- date: new Date(datum.endedAtInMilliseconds).valueOf(),
- cumulativeAmount: datum.cumulativeAmount,
- })
- )
+ ? periodTradingRewards.reverse().map(
+ (datum): TradingRewardsDatum => ({
+ date: new Date(datum.endedAtInMilliseconds).valueOf(),
+ cumulativeAmount: datum.cumulativeAmount,
+ })
+ )
: [],
[periodTradingRewards, canViewAccount]
);
diff --git a/src/views/dialogs/AdjustIsolatedMarginDialog.tsx b/src/views/dialogs/AdjustIsolatedMarginDialog.tsx
index 2236ac587..789ecb2e7 100644
--- a/src/views/dialogs/AdjustIsolatedMarginDialog.tsx
+++ b/src/views/dialogs/AdjustIsolatedMarginDialog.tsx
@@ -3,7 +3,7 @@ import { useCallback } from 'react';
import { shallowEqual } from 'react-redux';
import styled from 'styled-components';
-import type { SubaccountPosition } from '@/constants/abacus';
+import { AdjustIsolatedMarginDialogProps, DialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks/useStringGetter';
@@ -18,12 +18,10 @@ import { useAppSelector } from '@/state/appTypes';
import { AdjustIsolatedMarginForm } from '../forms/AdjustIsolatedMarginForm';
-type ElementProps = {
- positionId: SubaccountPosition['id'];
- setIsOpen?: (open: boolean) => void;
-};
-
-export const AdjustIsolatedMarginDialog = ({ positionId, setIsOpen }: ElementProps) => {
+export const AdjustIsolatedMarginDialog = ({
+ positionId,
+ setIsOpen,
+}: DialogProps) => {
const stringGetter = useStringGetter();
const subaccountPosition = useAppSelector(getOpenPositionFromId(positionId), shallowEqual);
diff --git a/src/views/dialogs/AdjustTargetLeverageDialog.tsx b/src/views/dialogs/AdjustTargetLeverageDialog.tsx
index 259d79ae3..884c39ca5 100644
--- a/src/views/dialogs/AdjustTargetLeverageDialog.tsx
+++ b/src/views/dialogs/AdjustTargetLeverageDialog.tsx
@@ -1,5 +1,6 @@
import styled from 'styled-components';
+import { AdjustTargetLeverageDialogProps, DialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks/useStringGetter';
@@ -10,11 +11,9 @@ import { Dialog } from '@/components/Dialog';
import { AdjustTargetLeverageForm } from '../forms/AdjustTargetLeverageForm';
-type ElementProps = {
- setIsOpen?: (open: boolean) => void;
-};
-
-export const AdjustTargetLeverageDialog = ({ setIsOpen }: ElementProps) => {
+export const AdjustTargetLeverageDialog = ({
+ setIsOpen,
+}: DialogProps) => {
const stringGetter = useStringGetter();
return (
diff --git a/src/views/dialogs/CancelAllOrdersDialog.tsx b/src/views/dialogs/CancelAllOrdersDialog.tsx
index 797e61c27..ad55d5286 100644
--- a/src/views/dialogs/CancelAllOrdersDialog.tsx
+++ b/src/views/dialogs/CancelAllOrdersDialog.tsx
@@ -1,7 +1,8 @@
import { useCallback, useMemo } from 'react';
-import { shallowEqual, useSelector } from 'react-redux';
+import { shallowEqual } from 'react-redux';
+import { CancelPendingOrdersDialogProps, DialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks/useStringGetter';
@@ -10,17 +11,16 @@ import { AssetIcon } from '@/components/AssetIcon';
import { Dialog } from '@/components/Dialog';
import { getNonZeroPendingPositions } from '@/state/accountSelectors';
+import { useAppSelector } from '@/state/appTypes';
import { CancelAllOrdersInMarketForm } from '../forms/CancelAllOrdersInMarketForm';
-type CancelAllOrdersDialogProps = {
- setIsOpen?: (open: boolean) => void;
- marketId: string;
-};
-
-export const CancelAllOrdersDialog = ({ setIsOpen, marketId }: CancelAllOrdersDialogProps) => {
+export const CancelAllOrdersDialog = ({
+ setIsOpen,
+ marketId,
+}: DialogProps) => {
const stringGetter = useStringGetter();
- const allPending = useSelector(getNonZeroPendingPositions, shallowEqual);
+ const allPending = useAppSelector(getNonZeroPendingPositions, shallowEqual);
const pendingPosition = useMemo(
() => allPending?.find((p) => p.marketId === marketId),
[allPending, marketId]
diff --git a/src/views/dialogs/ClosePositionDialog.tsx b/src/views/dialogs/ClosePositionDialog.tsx
index 36e124a1f..633d37b82 100644
--- a/src/views/dialogs/ClosePositionDialog.tsx
+++ b/src/views/dialogs/ClosePositionDialog.tsx
@@ -3,6 +3,7 @@ import { useState } from 'react';
import { shallowEqual } from 'react-redux';
import styled, { css } from 'styled-components';
+import { ClosePositionDialogProps, DialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { MobilePlaceOrderSteps } from '@/constants/trade';
@@ -28,11 +29,7 @@ import { getCurrentMarketData } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus';
import { MustBigNumber } from '@/lib/numbers';
-type ElementProps = {
- setIsOpen?: (open: boolean) => void;
-};
-
-export const ClosePositionDialog = ({ setIsOpen }: ElementProps) => {
+export const ClosePositionDialog = ({ setIsOpen }: DialogProps) => {
const { id } = useAppSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const { isTablet } = useBreakpoints();
const stringGetter = useStringGetter();
diff --git a/src/views/dialogs/ComplianceConfigDialog.tsx b/src/views/dialogs/ComplianceConfigDialog.tsx
index 396a70903..e2883485c 100644
--- a/src/views/dialogs/ComplianceConfigDialog.tsx
+++ b/src/views/dialogs/ComplianceConfigDialog.tsx
@@ -6,6 +6,7 @@ import styled from 'styled-components';
import type { Compliance } from '@/constants/abacus';
import { ComplianceStatus } from '@/constants/abacus';
import { ButtonAction } from '@/constants/buttons';
+import { ComplianceConfigDialogProps, DialogProps } from '@/constants/dialogs';
import { BLOCKED_COUNTRIES, CountryCodes, OFAC_SANCTIONED_COUNTRIES } from '@/constants/geo';
import { MenuGroup } from '@/constants/menus';
@@ -84,11 +85,7 @@ const usePreferenceMenu = () => {
return [otherSection, notificationSection];
};
-type ElementProps = {
- setIsOpen: (open: boolean) => void;
-};
-
-export const ComplianceConfigDialog = ({ setIsOpen }: ElementProps) => {
+export const ComplianceConfigDialog = ({ setIsOpen }: DialogProps) => {
const preferenceItems = usePreferenceMenu();
const complianceStatus = useAppSelector(getComplianceStatus, shallowEqual);
diff --git a/src/views/dialogs/DepositDialog.tsx b/src/views/dialogs/DepositDialog.tsx
index 0863db0a4..733695ff4 100644
--- a/src/views/dialogs/DepositDialog.tsx
+++ b/src/views/dialogs/DepositDialog.tsx
@@ -1,3 +1,4 @@
+import { DepositDialogProps, DialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints } from '@/hooks/useBreakpoints';
@@ -7,11 +8,7 @@ import { Dialog, DialogPlacement } from '@/components/Dialog';
import { DepositDialogContent } from './DepositDialog/DepositDialogContent';
-type ElementProps = {
- setIsOpen?: (open: boolean) => void;
-};
-
-export const DepositDialog = ({ setIsOpen }: ElementProps) => {
+export const DepositDialog = ({ setIsOpen }: DialogProps) => {
const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();
diff --git a/src/views/dialogs/DepositDialog/DepositDialogContent.tsx b/src/views/dialogs/DepositDialog/DepositDialogContent.tsx
index adc4ac8b5..e50edfd16 100644
--- a/src/views/dialogs/DepositDialog/DepositDialogContent.tsx
+++ b/src/views/dialogs/DepositDialog/DepositDialogContent.tsx
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
import styled from 'styled-components';
import { TransferInputField, TransferType } from '@/constants/abacus';
-import { AnalyticsEvent } from '@/constants/analytics';
+import { AnalyticsEvents } from '@/constants/analytics';
import { isMainnet } from '@/constants/networks';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -40,14 +40,14 @@ export const DepositDialogContent = ({ onDeposit }: ElementProps) => {
{isMainnet || !showFaucet ? (
{
- track(AnalyticsEvent.TransferDeposit, event);
+ track(AnalyticsEvents.TransferDeposit(event ?? {}));
onDeposit?.();
}}
/>
) : (
{
- track(AnalyticsEvent.TransferFaucet);
+ track(AnalyticsEvents.TransferFaucet());
onDeposit?.();
}}
/>
diff --git a/src/views/dialogs/DetailsDialog/FillDetailsDialog.tsx b/src/views/dialogs/DetailsDialog/FillDetailsDialog.tsx
index bdb6868bd..862a416e6 100644
--- a/src/views/dialogs/DetailsDialog/FillDetailsDialog.tsx
+++ b/src/views/dialogs/DetailsDialog/FillDetailsDialog.tsx
@@ -1,6 +1,7 @@
import { DateTime } from 'luxon';
import styled from 'styled-components';
+import { DialogProps, FillDetailsDialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useParameterizedSelector } from '@/hooks/useParameterizedSelector';
@@ -18,12 +19,7 @@ import { getSelectedLocale } from '@/state/localizationSelectors';
import { MustBigNumber } from '@/lib/numbers';
-type ElementProps = {
- fillId: string;
- setIsOpen: (open: boolean) => void;
-};
-
-export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => {
+export const FillDetailsDialog = ({ fillId, setIsOpen }: DialogProps) => {
const stringGetter = useStringGetter();
const selectedLocale = useAppSelector(getSelectedLocale);
diff --git a/src/views/dialogs/DetailsDialog/OrderDetailsDialog.tsx b/src/views/dialogs/DetailsDialog/OrderDetailsDialog.tsx
index d8b25a00e..e7ef1d940 100644
--- a/src/views/dialogs/DetailsDialog/OrderDetailsDialog.tsx
+++ b/src/views/dialogs/DetailsDialog/OrderDetailsDialog.tsx
@@ -9,6 +9,7 @@ import {
type Nullable,
} from '@/constants/abacus';
import { ButtonAction } from '@/constants/buttons';
+import { DialogProps, OrderDetailsDialogProps } from '@/constants/dialogs';
import { STRING_KEYS, type StringKey } from '@/constants/localization';
import { isMainnet } from '@/constants/networks';
import { CancelOrderStatuses } from '@/constants/trade';
@@ -37,12 +38,10 @@ import { MustBigNumber } from '@/lib/numbers';
import { isMarketOrderType, isOrderStatusClearable, relativeTimeString } from '@/lib/orders';
import { getMarginModeFromSubaccountNumber } from '@/lib/tradeData';
-type ElementProps = {
- orderId: string;
- setIsOpen: (open: boolean) => void;
-};
-
-export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
+export const OrderDetailsDialog = ({
+ orderId,
+ setIsOpen,
+}: DialogProps) => {
const stringGetter = useStringGetter();
const dispatch = useAppDispatch();
const selectedLocale = useAppSelector(getSelectedLocale);
diff --git a/src/views/dialogs/DisconnectDialog.tsx b/src/views/dialogs/DisconnectDialog.tsx
index 7eaf89f72..17498e9c6 100644
--- a/src/views/dialogs/DisconnectDialog.tsx
+++ b/src/views/dialogs/DisconnectDialog.tsx
@@ -2,6 +2,7 @@ import { Close } from '@radix-ui/react-dialog';
import styled from 'styled-components';
import { ButtonAction } from '@/constants/buttons';
+import { DialogProps, DisconnectWalletDialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useAccounts } from '@/hooks/useAccounts';
@@ -15,11 +16,7 @@ import { Dialog } from '@/components/Dialog';
import { useAppDispatch } from '@/state/appTypes';
import { closeDialog } from '@/state/dialogs';
-type ElementProps = {
- setIsOpen?: (open: boolean) => void;
-};
-
-export const DisconnectDialog = ({ setIsOpen }: ElementProps) => {
+export const DisconnectDialog = ({ setIsOpen }: DialogProps) => {
const stringGetter = useStringGetter();
const dispatch = useAppDispatch();
diff --git a/src/views/dialogs/DisplaySettingsDialog.tsx b/src/views/dialogs/DisplaySettingsDialog.tsx
index 8575fa2c8..48cf76d81 100644
--- a/src/views/dialogs/DisplaySettingsDialog.tsx
+++ b/src/views/dialogs/DisplaySettingsDialog.tsx
@@ -1,6 +1,7 @@
import { Indicator, Item, Root } from '@radix-ui/react-radio-group';
import styled, { css } from 'styled-components';
+import { DialogProps, DisplaySettingsDialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks/useStringGetter';
@@ -23,11 +24,7 @@ import {
} from '@/state/configs';
import { getAppColorMode, getAppThemeSetting } from '@/state/configsSelectors';
-type ElementProps = {
- setIsOpen: (open: boolean) => void;
-};
-
-export const DisplaySettingsDialog = ({ setIsOpen }: ElementProps) => {
+export const DisplaySettingsDialog = ({ setIsOpen }: DialogProps) => {
const dispatch = useAppDispatch();
const stringGetter = useStringGetter();
diff --git a/src/views/dialogs/ExchangeOfflineDialog.tsx b/src/views/dialogs/ExchangeOfflineDialog.tsx
index bad4c9e5c..15f8f526f 100644
--- a/src/views/dialogs/ExchangeOfflineDialog.tsx
+++ b/src/views/dialogs/ExchangeOfflineDialog.tsx
@@ -4,7 +4,7 @@ import { shallowEqual } from 'react-redux';
import styled from 'styled-components';
import { AbacusApiStatus } from '@/constants/abacus';
-import { DialogTypes } from '@/constants/dialogs';
+import { DialogProps, DialogTypes, ExchangeOfflineDialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { isDev } from '@/constants/networks';
@@ -21,12 +21,10 @@ import { useAppDispatch, useAppSelector } from '@/state/appTypes';
import { closeDialog } from '@/state/dialogs';
import { getActiveDialog } from '@/state/dialogsSelectors';
-type ElementProps = {
- preventClose?: boolean;
- setIsOpen?: (open: boolean) => void;
-};
-
-export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps) => {
+export const ExchangeOfflineDialog = ({
+ preventClose,
+ setIsOpen,
+}: DialogProps) => {
const dispatch = useAppDispatch();
const stringGetter = useStringGetter();
const { status, statusErrorMessage } = useApiState();
@@ -34,7 +32,11 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
const activeDialog = useAppSelector(getActiveDialog, shallowEqual);
useEffect(() => {
- if (activeDialog?.type === DialogTypes.ExchangeOffline && status === AbacusApiStatus.NORMAL) {
+ if (
+ activeDialog != null &&
+ DialogTypes.is.ExchangeOffline(activeDialog) &&
+ status === AbacusApiStatus.NORMAL
+ ) {
dispatch(closeDialog());
}
}, [status, selectedNetwork]);
diff --git a/src/views/dialogs/ExternalLinkDialog.tsx b/src/views/dialogs/ExternalLinkDialog.tsx
index fe2ee2ab6..9f02e9697 100644
--- a/src/views/dialogs/ExternalLinkDialog.tsx
+++ b/src/views/dialogs/ExternalLinkDialog.tsx
@@ -1,8 +1,7 @@
-import type { ReactNode } from 'react';
-
import styled from 'styled-components';
import { ButtonAction, ButtonType } from '@/constants/buttons';
+import { DialogProps, ExternalLinkDialogProps } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks/useStringGetter';
@@ -12,15 +11,6 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { Button } from '@/components/Button';
import { Dialog } from '@/components/Dialog';
-type ElementProps = {
- buttonText?: ReactNode;
- link: string;
- linkDescription?: string;
- title?: ReactNode;
- slotContent?: ReactNode;
- setIsOpen: (open: boolean) => void;
-};
-
export const ExternalLinkDialog = ({
setIsOpen,
buttonText,
@@ -28,7 +18,7 @@ export const ExternalLinkDialog = ({
linkDescription,
title,
slotContent,
-}: ElementProps) => {
+}: DialogProps) => {
const stringGetter = useStringGetter();
return (