From ed61b3112ecb8cf4b980eea412ad14ff438f92f1 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Fri, 18 Aug 2023 15:49:13 -0400 Subject: [PATCH 1/8] rename tokens to dv4tnt, fix header dropdown hover state, add entry points to receive/send dialogs in account menu --- src/constants/routes.ts | 2 +- src/constants/wallets.ts | 2 +- src/pages/rewards/DYDXBalancePanel.tsx | 6 +-- src/styles/popoverMixins.ts | 2 + src/views/dialogs/ReceiveDialog.tsx | 8 +-- src/views/dialogs/TransferDialog.tsx | 6 ++- src/views/forms/TransferForm.tsx | 12 +++-- src/views/menus/AccountMenu.tsx | 75 ++++++++++++++++++++------ 8 files changed, 84 insertions(+), 29 deletions(-) diff --git a/src/constants/routes.ts b/src/constants/routes.ts index bd8642a..a7f415d 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -5,7 +5,7 @@ export enum AppRoute { Profile = '/profile', Alerts = '/alerts', Settings = '/settings', - Rewards = '/DYDX', + Rewards = '/DV4TNT', } export enum PortfolioRoute { diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts index 6451456..8e5299b 100644 --- a/src/constants/wallets.ts +++ b/src/constants/wallets.ts @@ -377,7 +377,7 @@ export const DYDX_CHAIN_INFO: Parameters[0] = { export enum DydxChainAsset { USDC = 'USDC', - DYDX = 'DYDX', + DYDX = 'Dv4TNT', } export const DYDX_CHAIN_ASSET_COIN_DENOM: Record = { diff --git a/src/pages/rewards/DYDXBalancePanel.tsx b/src/pages/rewards/DYDXBalancePanel.tsx index 7393217..5ebf66b 100644 --- a/src/pages/rewards/DYDXBalancePanel.tsx +++ b/src/pages/rewards/DYDXBalancePanel.tsx @@ -36,8 +36,8 @@ export const DYDXBalancePanel = () => { slotHeader={ - - DYDX + {/* */} + Dv4TNT {!canAccountTrade ? ( @@ -106,7 +106,7 @@ export const DYDXBalancePanel = () => { { key: 'totalBalance', label: 'Total balance', - value: , + value: , }, ]} /> diff --git a/src/styles/popoverMixins.ts b/src/styles/popoverMixins.ts index 3f01d3c..47932cf 100644 --- a/src/styles/popoverMixins.ts +++ b/src/styles/popoverMixins.ts @@ -185,6 +185,7 @@ export const popoverMixins = { --item-checked-backgroundColor: var(--color-layer-2); --item-checked-textColor: currentColor; + --item-highlighted-backgroundColor: var(--color-layer-2); --item-highlighted-textColor: var(--color-text-2); --item-radius: 0px; @@ -218,6 +219,7 @@ export const popoverMixins = { &[data-highlighted] // @radix-ui { filter: brightness(1.1); + background-color: var(--item-highlighted-backgroundColor); color: var(--item-highlighted-textColor, var(--trigger-textColor, inherit)) !important; outline: none; } diff --git a/src/views/dialogs/ReceiveDialog.tsx b/src/views/dialogs/ReceiveDialog.tsx index 957bcb6..0f8db5e 100644 --- a/src/views/dialogs/ReceiveDialog.tsx +++ b/src/views/dialogs/ReceiveDialog.tsx @@ -22,14 +22,15 @@ import { truncateAddress } from '@/lib/wallet'; import { OnboardingTriggerButton } from './OnboardingTriggerButton'; type ElementProps = { + selectedAsset?: DydxChainAsset; setIsOpen: (open: boolean) => void; }; -export const ReceiveDialog = ({ setIsOpen }: ElementProps) => { +export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.DYDX, setIsOpen }: ElementProps) => { const stringGetter = useStringGetter(); const { dydxAddress } = useAccounts(); - const [asset, setAsset] = useState(DydxChainAsset.DYDX); + const [asset, setAsset] = useState(selectedAsset); const [copied, setCopied] = useState(false); const onCopy = () => { @@ -52,7 +53,8 @@ export const ReceiveDialog = ({ setIsOpen }: ElementProps) => { value: DydxChainAsset.DYDX, label: ( - DYDX + {/* */} + Dv4TNT ), }, diff --git a/src/views/dialogs/TransferDialog.tsx b/src/views/dialogs/TransferDialog.tsx index 4af8e48..fd0bb70 100644 --- a/src/views/dialogs/TransferDialog.tsx +++ b/src/views/dialogs/TransferDialog.tsx @@ -1,21 +1,23 @@ import styled, { type AnyStyledComponent } from 'styled-components'; import { STRING_KEYS } from '@/constants/localization'; +import { DydxChainAsset } from '@/constants/wallets'; import { useStringGetter } from '@/hooks'; import { Dialog } from '@/components/Dialog'; import { TransferForm } from '@/views/forms/TransferForm'; type ElementProps = { + selectedAsset?: DydxChainAsset; setIsOpen?: (open: boolean) => void; }; -export const TransferDialog = ({ setIsOpen }: ElementProps) => { +export const TransferDialog = ({ selectedAsset, setIsOpen }: ElementProps) => { const stringGetter = useStringGetter(); return ( - setIsOpen?.(false)} /> + setIsOpen?.(false)} /> ); }; diff --git a/src/views/forms/TransferForm.tsx b/src/views/forms/TransferForm.tsx index 966bea1..2131411 100644 --- a/src/views/forms/TransferForm.tsx +++ b/src/views/forms/TransferForm.tsx @@ -48,6 +48,7 @@ import { MustBigNumber } from '@/lib/numbers'; import { log } from '@/lib/telemetry'; type TransferFormProps = { + selectedAsset?: DydxChainAsset; onDone?: () => void; className?: string; }; @@ -74,7 +75,11 @@ const debouncedEstimateFee = debounce( { trailing: true } ); -export const TransferForm = ({ onDone, className }: TransferFormProps) => { +export const TransferForm = ({ + selectedAsset = DydxChainAsset.DYDX, + onDone, + className, +}: TransferFormProps) => { const stringGetter = useStringGetter(); const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {}; const { dydxAddress } = useAccounts(); @@ -84,7 +89,7 @@ export const TransferForm = ({ onDone, className }: TransferFormProps) => { const { selectedNetwork } = useSelectedNetwork(); // User Input - const [asset, setAsset] = useState(DydxChainAsset.DYDX); + const [asset, setAsset] = useState(selectedAsset); // Form states const [error, setError] = useState(); @@ -214,7 +219,8 @@ export const TransferForm = ({ onDone, className }: TransferFormProps) => { value: DydxChainAsset.DYDX, label: ( - DYDX + {/* */} + Dv4TNT ), }, diff --git a/src/views/menus/AccountMenu.tsx b/src/views/menus/AccountMenu.tsx index a1dc135..fc882df 100644 --- a/src/views/menus/AccountMenu.tsx +++ b/src/views/menus/AccountMenu.tsx @@ -5,7 +5,7 @@ import { OnboardingState } from '@/constants/account'; import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons'; import { DialogTypes } from '@/constants/dialogs'; import { STRING_KEYS, TOOLTIP_STRING_KEYS } from '@/constants/localization'; -import { wallets } from '@/constants/wallets'; +import { DydxChainAsset, wallets } from '@/constants/wallets'; import { useAccounts, useBreakpoints, useStringGetter, useAccountBalance } from '@/hooks'; @@ -103,22 +103,60 @@ export const AccountMenu = () => {
- - {stringGetter({ key: STRING_KEYS.ASSET_BALANCE, params: { ASSET: 'Dv4TNT' } })} - {/* */} - - +
+ + {stringGetter({ key: STRING_KEYS.ASSET_BALANCE, params: { ASSET: 'Dv4TNT' } })} + {/* */} + + +
+ dispatch(openDialog({ type: DialogTypes.Transfer }))} + shape={ButtonShape.Square} + />
- - {stringGetter({ key: STRING_KEYS.ASSET_BALANCE, params: { ASSET: 'USDC' } })} - - - +
+ + {stringGetter({ key: STRING_KEYS.ASSET_BALANCE, params: { ASSET: 'USDC' } })} + + + +
+ + + dispatch( + openDialog({ + type: DialogTypes.Receive, + dialogProps: { selectedAsset: DydxChainAsset.USDC }, + }) + ) + } + shape={ButtonShape.Square} + /> + + dispatch( + openDialog({ + type: DialogTypes.Transfer, + dialogProps: { selectedAsset: DydxChainAsset.USDC }, + }) + ) + } + shape={ButtonShape.Square} + /> +
@@ -204,6 +242,10 @@ Styled.Column = styled.div` ${layoutMixins.column} `; +Styled.InlineRow = styled.div` + ${layoutMixins.inlineRow} +`; + Styled.AddressRow = styled.div` ${layoutMixins.row} @@ -256,7 +298,7 @@ Styled.Balances = styled.div` gap: 2px; > div { - ${layoutMixins.flexColumn} + ${layoutMixins.spacedRow} gap: 0.5rem; padding: 0.5rem 1rem; @@ -281,6 +323,7 @@ Styled.DropdownMenu = styled(DropdownMenu)` --dropdownMenu-item-font-size: 0.875rem; --popover-padding: 0 0 0.5rem 0; + --trigger-hover-backgroundColor: var(--color-layer-3); `; Styled.WarningIcon = styled(Icon)` From 3dc87e873dfffb52e03c092cff8139fc88a02ba0 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Mon, 21 Aug 2023 15:55:52 -0400 Subject: [PATCH 2/8] show receive button for both assets --- src/views/menus/AccountMenu.tsx | 61 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/src/views/menus/AccountMenu.tsx b/src/views/menus/AccountMenu.tsx index fc882df..27d886f 100644 --- a/src/views/menus/AccountMenu.tsx +++ b/src/views/menus/AccountMenu.tsx @@ -1,5 +1,7 @@ +import { memo } from 'react'; import styled, { AnyStyledComponent } from 'styled-components'; import { shallowEqual, useDispatch, useSelector } from 'react-redux'; +import type { Dispatch } from '@reduxjs/toolkit'; import { OnboardingState } from '@/constants/account'; import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons'; @@ -110,12 +112,7 @@ export const AccountMenu = () => { - dispatch(openDialog({ type: DialogTypes.Transfer }))} - shape={ButtonShape.Square} - /> +
@@ -129,34 +126,7 @@ export const AccountMenu = () => { fractionDigits={2} />
- - - dispatch( - openDialog({ - type: DialogTypes.Receive, - dialogProps: { selectedAsset: DydxChainAsset.USDC }, - }) - ) - } - shape={ButtonShape.Square} - /> - - dispatch( - openDialog({ - type: DialogTypes.Transfer, - dialogProps: { selectedAsset: DydxChainAsset.USDC }, - }) - ) - } - shape={ButtonShape.Square} - /> - +
@@ -229,6 +199,29 @@ export const AccountMenu = () => { ); }; +const AssetActions = memo(({ asset, dispatch }: { asset: DydxChainAsset; dispatch: Dispatch }) => ( + + {[ + // TODO(@rosepuppy): Add withdraw action for USDC + { + dialogType: DialogTypes.Receive, + iconName: IconName.Qr, + }, + { dialogType: DialogTypes.Transfer, iconName: IconName.Send }, + ].map(({ iconName, dialogType }) => ( + + dispatch(openDialog({ type: dialogType, dialogProps: { selectedAsset: asset } })) + } + shape={ButtonShape.Square} + /> + ))} + +)); + const Styled: Record = {}; Styled.AccountInfo = styled.div` From 32b375cfa450fb17a3620f11842cc803599e2e07 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Wed, 23 Aug 2023 18:01:28 -0400 Subject: [PATCH 3/8] address feedback --- src/styles/headerMixins.ts | 2 +- src/views/menus/AccountMenu.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/styles/headerMixins.ts b/src/styles/headerMixins.ts index 8efef47..4a8da3d 100644 --- a/src/styles/headerMixins.ts +++ b/src/styles/headerMixins.ts @@ -5,7 +5,7 @@ export const headerMixins = { --trigger-backgroundColor: transparent; --trigger-textColor: var(--color-text-0); - --trigger-hover-backgroundColor: var(--color-layer-4); + --trigger-hover-backgroundColor: var(--color-layer-3); --trigger-hover-textColor: var(--color-text-2); --trigger-open-backgroundColor: var(--color-layer-1); diff --git a/src/views/menus/AccountMenu.tsx b/src/views/menus/AccountMenu.tsx index 27d886f..cc11f8a 100644 --- a/src/views/menus/AccountMenu.tsx +++ b/src/views/menus/AccountMenu.tsx @@ -316,7 +316,6 @@ Styled.DropdownMenu = styled(DropdownMenu)` --dropdownMenu-item-font-size: 0.875rem; --popover-padding: 0 0 0.5rem 0; - --trigger-hover-backgroundColor: var(--color-layer-3); `; Styled.WarningIcon = styled(Icon)` From 76819e08396425755df17dfaabff38521b02b439 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Wed, 23 Aug 2023 18:54:39 -0400 Subject: [PATCH 4/8] signed commits --- src/views/menus/AccountMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/menus/AccountMenu.tsx b/src/views/menus/AccountMenu.tsx index cc11f8a..cbbfd62 100644 --- a/src/views/menus/AccountMenu.tsx +++ b/src/views/menus/AccountMenu.tsx @@ -212,11 +212,11 @@ const AssetActions = memo(({ asset, dispatch }: { asset: DydxChainAsset; dispatc dispatch(openDialog({ type: dialogType, dialogProps: { selectedAsset: asset } })) } - shape={ButtonShape.Square} /> ))} From 2d0a944097cda50f226e7644c7ea017182e69618 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Mon, 21 Aug 2023 17:17:48 -0400 Subject: [PATCH 5/8] transfers history table --- src/constants/abacus.ts | 2 + src/constants/localization/app.ts | 2 + src/lib/abacus/stateNotification.ts | 6 + src/localization/en/app.json | 2 + src/pages/portfolio/History.tsx | 11 +- src/pages/portfolio/Portfolio.tsx | 8 +- src/pages/portfolio/PortfolioNavMobile.tsx | 12 +- src/state/account.ts | 7 + src/state/accountSelectors.ts | 6 + src/styles/tradeViewMixins.ts | 4 + src/views/tables/TransferHistoryTable.tsx | 184 +++++++++++++++++++++ 11 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 src/views/tables/TransferHistoryTable.tsx diff --git a/src/constants/abacus.ts b/src/constants/abacus.ts index 55c0ad7..551274c 100644 --- a/src/constants/abacus.ts +++ b/src/constants/abacus.ts @@ -112,6 +112,8 @@ export type SubaccountFill = Abacus.exchange.dydx.abacus.output.SubaccountFill; export type SubaccountFundingPayment = Abacus.exchange.dydx.abacus.output.SubaccountFundingPayment; export type SubaccountFundingPayments = Abacus.exchange.dydx.abacus.output.SubaccountFundingPayment[]; +export type SubaccountTransfer = Abacus.exchange.dydx.abacus.output.SubaccountTransfer; +export type SubaccountTransfers = Abacus.exchange.dydx.abacus.output.SubaccountTransfer[]; // ------ Historical PnL ------ // export type SubAccountHistoricalPNL = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL; diff --git a/src/constants/localization/app.ts b/src/constants/localization/app.ts index 93914c9..a392340 100644 --- a/src/constants/localization/app.ts +++ b/src/constants/localization/app.ts @@ -177,6 +177,7 @@ export const APP_STRING_KEYS = { RECEIVE: 'GENERAL.RECEIVE', RECENT: 'GENERAL.RECENT', RECENT_TRADES_SHORT: 'GENERAL.RECENT_TRADES_SHORT', + RECIPIENT: 'GENERAL.RECIPIENT', REFERRAL_CODE: 'GENERAL.REFERRAL_CODE', REFERRALS: 'GENERAL.REFERRALS', REFERRER_PERCENT_OFF: 'GENERAL.REFERRER_PERCENT_OFF', @@ -188,6 +189,7 @@ export const APP_STRING_KEYS = { SELECT_NETWORK: 'GENERAL.SELECT_NETWORK', SELL: 'GENERAL.SELL', SEND: 'GENERAL.SEND', + SENDER: 'GENERAL.SENDER', SHARE: 'GENERAL.SHARE', SHORT_POSITION_SHORT: 'GENERAL.SHORT_POSITION_SHORT', SIDE: 'GENERAL.SIDE', diff --git a/src/lib/abacus/stateNotification.ts b/src/lib/abacus/stateNotification.ts index ef08749..fbadc75 100644 --- a/src/lib/abacus/stateNotification.ts +++ b/src/lib/abacus/stateNotification.ts @@ -19,6 +19,7 @@ import { setFundingPayments, setHistoricalPnl, setSubaccount, + setTransfers, setWallet, } from '@/state/account'; @@ -102,6 +103,11 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol { dispatch(setFundingPayments(fundingPayments)); } + if (changes.has(Changes.transfers)) { + const transfers = updatedState.subaccountTransfers(subaccountId)?.toArray() || []; + dispatch(setTransfers(transfers)); + } + if (changes.has(Changes.historicalPnl)) { const historicalPnl = updatedState.subaccountHistoricalPnl(subaccountId)?.toArray() || []; diff --git a/src/localization/en/app.json b/src/localization/en/app.json index 41a0027..a492d6b 100644 --- a/src/localization/en/app.json +++ b/src/localization/en/app.json @@ -182,6 +182,7 @@ "RECEIVE": "Receive", "RECENT": "Recent", "RECENT_TRADES_SHORT": "Trades", + "RECIPIENT": "Recipient", "REFERRAL_CODE": "Referral Code", "REFERRALS": "Referrals", "REFERRER_PERCENT_OFF": "{DISCOUNT}% off", @@ -193,6 +194,7 @@ "SELECT_NETWORK": "Select Network", "SELL": "Sell", "SEND": "Send", + "SENDER": "Sender", "SHARE": "Share", "SHORT_POSITION_SHORT": "Short", "SIDE": "Side", diff --git a/src/pages/portfolio/History.tsx b/src/pages/portfolio/History.tsx index 72fffa2..78f85aa 100644 --- a/src/pages/portfolio/History.tsx +++ b/src/pages/portfolio/History.tsx @@ -28,13 +28,14 @@ export const History = () => { label:

{stringGetter({ key: STRING_KEYS.TRADES })}

, href: HistoryRoute.Trades, }, + { + value: HistoryRoute.Transfers, + label:

{stringGetter({ key: STRING_KEYS.TRANSFERS })}

, + href: HistoryRoute.Transfers, + tag: 'USDC', + }, // TODO - TRCL-1693 - // { - // value: HistoryRoute.Transfers, - // label:

{stringGetter({ key: STRING_KEYS.TRANSFERS })}

, - // href: HistoryRoute.Transfers, - // }, - // { // value: HistoryRoute.Payments, // label:

{stringGetter({ key: STRING_KEYS.PAYMENTS })}

, // href: HistoryRoute.Payments, diff --git a/src/pages/portfolio/Portfolio.tsx b/src/pages/portfolio/Portfolio.tsx index 227af41..d8969b8 100644 --- a/src/pages/portfolio/Portfolio.tsx +++ b/src/pages/portfolio/Portfolio.tsx @@ -9,6 +9,7 @@ import { useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks'; import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable'; import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable'; +import { TransferHistoryTable } from '@/views/tables/TransferHistoryTable'; import { Icon, IconName } from '@/components/Icon'; import { NavigationMenu } from '@/components/NavigationMenu'; import { WithSidebar } from '@/components/WithSidebar'; @@ -61,7 +62,10 @@ export default () => { /> } /> - } /> + } + /> } @@ -118,8 +122,6 @@ export default () => { }, ], }, - // TODO(aforaleka) Add back subitems when there are clearer designs - // or when transfers and payments are ready ]} /> ) diff --git a/src/pages/portfolio/PortfolioNavMobile.tsx b/src/pages/portfolio/PortfolioNavMobile.tsx index ebfaf5a..0ce1a65 100644 --- a/src/pages/portfolio/PortfolioNavMobile.tsx +++ b/src/pages/portfolio/PortfolioNavMobile.tsx @@ -40,12 +40,12 @@ export const PortfolioNavMobile = () => { label: stringGetter({ key: STRING_KEYS.TRADES }), description: stringGetter({ key: STRING_KEYS.TRADES_DESCRIPTION }), }, - // TODO: TRCL-1693 - re-enable when Payments and Transfers are ready - // { - // value: `${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Transfers}`, - // label: stringGetter({ key: STRING_KEYS.TRANSFERS }), - // description: stringGetter({ key: STRING_KEYS.TRANSFERS_DESCRIPTION }), - // }, + { + value: `${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Transfers}`, + label: stringGetter({ key: STRING_KEYS.TRANSFERS }), + description: stringGetter({ key: STRING_KEYS.TRANSFERS_DESCRIPTION }), + }, + // TODO: TRCL-1693 - re-enable when Payments are ready // { // value: `${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Payments}`, // label: stringGetter({ key: STRING_KEYS.PAYMENTS }), diff --git a/src/state/account.ts b/src/state/account.ts index b722363..5fab6e3 100644 --- a/src/state/account.ts +++ b/src/state/account.ts @@ -9,6 +9,7 @@ import type { SubaccountFundingPayments, Wallet, SubaccountOrder, + SubaccountTransfers, HistoricalPnlPeriods, SubAccountHistoricalPNLs, } from '@/constants/abacus'; @@ -22,6 +23,7 @@ import { getLocalStorage } from '@/lib/localStorage'; export type AccountState = { fills?: SubaccountFills; fundingPayments?: SubaccountFundingPayments; + transfers?: SubaccountTransfers; clearedOrderIds?: string[]; uncommittedOrderClientIds?: number[]; hasUnseenFillUpdates: boolean; @@ -38,6 +40,7 @@ export type AccountState = { const initialState: AccountState = { fills: undefined, fundingPayments: undefined, + transfers: undefined, clearedOrderIds: undefined, uncommittedOrderClientIds: undefined, hasUnseenFillUpdates: false, @@ -81,6 +84,9 @@ export const accountSlice = createSlice({ setFundingPayments: (state, action: PayloadAction) => { state.fundingPayments = action.payload; }, + setTransfers: (state, action: PayloadAction) => { + state.transfers = action.payload; + }, clearOrder: (state, action: PayloadAction) => ({ ...state, clearedOrderIds: [...(state.clearedOrderIds || []), action.payload], @@ -148,6 +154,7 @@ export const accountSlice = createSlice({ export const { setFills, setFundingPayments, + setTransfers, clearOrder, setOnboardingGuard, setOnboardingState, diff --git a/src/state/accountSelectors.ts b/src/state/accountSelectors.ts index c16f039..29aa72e 100644 --- a/src/state/accountSelectors.ts +++ b/src/state/accountSelectors.ts @@ -193,6 +193,12 @@ export const getCurrentMarketFills = createSelector( !currentMarketId ? [] : marketFills[currentMarketId] ); +/** + * @param state + * @returns list of transfers for the currently connected subaccount + */ +export const getSubaccountTransfers = (state: RootState) => state.account?.transfers; + /** * @param state * @returns list of funding payments for the currently connected subaccount diff --git a/src/styles/tradeViewMixins.ts b/src/styles/tradeViewMixins.ts index fa64498..a847b81 100644 --- a/src/styles/tradeViewMixins.ts +++ b/src/styles/tradeViewMixins.ts @@ -18,5 +18,9 @@ export const tradeViewMixins: Record< tbody { font: var(--font-small-book); } + + thead tr { + box-shadow: none; + } `, }; diff --git a/src/views/tables/TransferHistoryTable.tsx b/src/views/tables/TransferHistoryTable.tsx new file mode 100644 index 0000000..f5df2a9 --- /dev/null +++ b/src/views/tables/TransferHistoryTable.tsx @@ -0,0 +1,184 @@ +import styled, { type AnyStyledComponent, css } from 'styled-components'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; +import type { ColumnSize } from '@react-types/table'; + +import { type SubaccountTransfer } from '@/constants/abacus'; +import { STRING_KEYS, StringGetterFunction } from '@/constants/localization'; + +import { useBreakpoints, useStringGetter } from '@/hooks'; + +import { layoutMixins } from '@/styles/layoutMixins'; +import { tradeViewMixins } from '@/styles/tradeViewMixins'; + +import { Icon } from '@/components/Icon'; +import { Output, OutputType } from '@/components/Output'; +import { Table, TableCell, TableColumnHeader, type ColumnDef } from '@/components/Table'; + +import { getSubaccountTransfers } from '@/state/accountSelectors'; + +import { truncateAddress } from '@/lib/wallet'; +import { Button } from '@/components/Button'; +import { ButtonAction } from '@/constants/buttons'; +import { openDialog } from '@/state/dialogs'; +import { DialogTypes } from '@/constants/dialogs'; +import { Link } from '@/components/Link'; +import { calculateCanAccountTrade } from '@/state/accountCalculators'; +import { OnboardingTriggerButton } from '../dialogs/OnboardingTriggerButton'; + +const MOBILE_TRANSFERS_PER_PAGE = 50; + +export enum TransferHistoryTableColumnKey { + Time = 'Time', + Action = 'Action', + SenderRecipient = 'Sender-Recipient', + Amount = 'Amount', + TxHash = 'TxHash', +} + +const getTransferHistoryTableColumnDef = ({ + key, + stringGetter, + width, +}: { + key: TransferHistoryTableColumnKey; + isTablet?: boolean; + stringGetter: StringGetterFunction; + width?: ColumnSize; +}): ColumnDef => ({ + width, + ...( + { + [TransferHistoryTableColumnKey.Time]: { + columnKey: TransferHistoryTableColumnKey.Time, + getCellValue: (row) => row.updatedAtMilliseconds, + label: stringGetter({ key: STRING_KEYS.TIME }), + renderCell: ({ updatedAtMilliseconds }) => ( + + ), + }, + [TransferHistoryTableColumnKey.Action]: { + columnKey: TransferHistoryTableColumnKey.Action, + getCellValue: (row) => row.resources.typeStringKey, + label: stringGetter({ key: STRING_KEYS.ACTION }), + renderCell: ({ resources }) => + resources.typeStringKey && stringGetter({ key: resources.typeStringKey }), + }, + [TransferHistoryTableColumnKey.SenderRecipient]: { + columnKey: TransferHistoryTableColumnKey.SenderRecipient, + getCellValue: (row) => `${row.fromAddress}-${row.toAddress}`, + label: ( + + {stringGetter({ key: STRING_KEYS.SENDER })} + {stringGetter({ key: STRING_KEYS.RECIPIENT })} + + ), + renderCell: ({ fromAddress, toAddress }) => ( + + {fromAddress ? truncateAddress(fromAddress) : '-'} + {toAddress ? truncateAddress(toAddress) : '-'} + + ), + }, + [TransferHistoryTableColumnKey.Amount]: { + columnKey: TransferHistoryTableColumnKey.Amount, + getCellValue: (row) => row.amount, + label: stringGetter({ key: STRING_KEYS.AMOUNT }), + renderCell: ({ amount }) => , + }, + [TransferHistoryTableColumnKey.TxHash]: { + columnKey: TransferHistoryTableColumnKey.TxHash, + getCellValue: (row) => row.transactionHash, + label: stringGetter({ key: STRING_KEYS.TRANSACTION }), + renderCell: ({ transactionHash }) => + transactionHash ? {transactionHash} : '-', + }, + } as Record> + )[key], +}); + +type ElementProps = { + columnKeys?: TransferHistoryTableColumnKey[]; + columnWidths?: Partial>; +}; + +type StyleProps = { + withOuterBorder?: boolean; + withInnerBorders?: boolean; +}; + +export const TransferHistoryTable = ({ + columnKeys = Object.values(TransferHistoryTableColumnKey), + columnWidths, + withOuterBorder, + withInnerBorders = true, +}: ElementProps & StyleProps) => { + const stringGetter = useStringGetter(); + const dispatch = useDispatch(); + const { isMobile, isTablet } = useBreakpoints(); + + const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual); + + const transfers = useSelector(getSubaccountTransfers, shallowEqual) ?? []; + + return ( + row.id} + columns={columnKeys.map((key: TransferHistoryTableColumnKey) => + getTransferHistoryTableColumnDef({ + key, + isTablet, + stringGetter, + width: columnWidths?.[key], + }) + )} + slotEmpty={ + <> + {stringGetter({ key: STRING_KEYS.TRANSFERS_EMPTY_STATE })} + {canAccountTrade ? ( + + ) : ( + + )} + + } + selectionBehavior="replace" + withOuterBorder={withOuterBorder} + withInnerBorders={withInnerBorders} + withScrollSnapColumns + withScrollSnapRows + /> + ); +}; + +const Styled: Record = {}; + +Styled.Table = styled(Table)` + ${tradeViewMixins.horizontalTable} +`; + +Styled.InlineRow = styled.div` + ${layoutMixins.inlineRow} +`; + +Styled.Icon = styled(Icon)` + font-size: 3em; +`; + +Styled.TimeOutput = styled(Output)` + color: var(--color-text-0); +`; + +Styled.TxHash = styled(Link)` + justify-content: flex-end; +`; From e30940e7dfdd46d8b17d419a0b007ee2ae9533d7 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Wed, 23 Aug 2023 17:53:47 -0400 Subject: [PATCH 6/8] bump abacus + make addresses copyable --- package.json | 2 +- pnpm-lock.yaml | 22 +++++----- src/views/tables/TransferHistoryTable.tsx | 53 ++++++++++++++++++++--- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 88420c0..c200637 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@cosmjs/encoding": "^0.31.0", "@cosmjs/proto-signing": "^0.31.0", "@cosmjs/stargate": "^0.31.0", - "@dydxprotocol/abacus": "^0.2.28", + "@dydxprotocol/abacus": "^0.2.37", "@cosmjs/tendermint-rpc": "^0.31.0", "@dydxprotocol/v4-client": "^0.29.0", "@ethersproject/providers": "^5.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83de3b2..a7f94ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - dependencies: '@0xsquid/sdk': specifier: ^1.7.2 @@ -27,8 +23,8 @@ dependencies: specifier: ^0.31.0 version: 0.31.0 '@dydxprotocol/abacus': - specifier: ^0.2.28 - version: 0.2.28 + specifier: ^0.2.37 + version: 0.2.37 '@dydxprotocol/v4-client': specifier: ^0.29.0 version: 0.29.0 @@ -1143,8 +1139,8 @@ packages: resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==} dev: true - /@dydxprotocol/abacus@0.2.28: - resolution: {integrity: sha512-mGmhwQM8aiV/cD+d3lvW76ysoPN7uVsDyAiVcRdzGqgj+gbM0IIJDYXG14xHruOTi+gK1Aga5MUz2pdvmPDhYA==} + /@dydxprotocol/abacus@0.2.37: + resolution: {integrity: sha512-amoq9aVXo+lVTQGu2jsyy5/0bniOMEXpyJDmaAr2TpbrtQtw2WgyAP+se9Q5wt3yh0Mzdtsh7P2h090kH7/bZQ==} dev: false /@dydxprotocol/dydxjs@0.3.0: @@ -8277,7 +8273,7 @@ packages: resolution: {integrity: sha512-WIdaQ8uW1vIbYvNnAVunkC6yxTrneJC7VQ5UUQ0kuw8b0C0A39KTIpoQHCfc8tV7o9vF4niwRhdXEdfAgQEsQQ==} dependencies: cosmos-directory-types: 0.0.6 - node-fetch-native: 1.2.0 + node-fetch-native: 1.4.0 dev: false /cosmos-directory-types@0.0.6: @@ -12462,8 +12458,8 @@ packages: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} - /node-fetch-native@1.2.0: - resolution: {integrity: sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==} + /node-fetch-native@1.4.0: + resolution: {integrity: sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==} dev: false /node-fetch@2.6.12: @@ -15855,3 +15851,7 @@ packages: release-it: 15.11.0 semver: 7.5.1 dev: false + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/src/views/tables/TransferHistoryTable.tsx b/src/views/tables/TransferHistoryTable.tsx index f5df2a9..59fa60b 100644 --- a/src/views/tables/TransferHistoryTable.tsx +++ b/src/views/tables/TransferHistoryTable.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import styled, { type AnyStyledComponent, css } from 'styled-components'; import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import type { ColumnSize } from '@react-types/table'; @@ -10,7 +11,7 @@ import { useBreakpoints, useStringGetter } from '@/hooks'; import { layoutMixins } from '@/styles/layoutMixins'; import { tradeViewMixins } from '@/styles/tradeViewMixins'; -import { Icon } from '@/components/Icon'; +import { Icon, IconName } from '@/components/Icon'; import { Output, OutputType } from '@/components/Output'; import { Table, TableCell, TableColumnHeader, type ColumnDef } from '@/components/Table'; @@ -78,8 +79,8 @@ const getTransferHistoryTableColumnDef = ({ ), renderCell: ({ fromAddress, toAddress }) => ( - {fromAddress ? truncateAddress(fromAddress) : '-'} - {toAddress ? truncateAddress(toAddress) : '-'} + + ), }, @@ -93,8 +94,14 @@ const getTransferHistoryTableColumnDef = ({ columnKey: TransferHistoryTableColumnKey.TxHash, getCellValue: (row) => row.transactionHash, label: stringGetter({ key: STRING_KEYS.TRANSACTION }), - renderCell: ({ transactionHash }) => - transactionHash ? {transactionHash} : '-', + renderCell: ({ transactionHash, resources }) => + transactionHash ? ( + + {truncateAddress(transactionHash, '')} + + ) : ( + '-' + ), }, } as Record> )[key], @@ -161,6 +168,26 @@ export const TransferHistoryTable = ({ ); }; +const CopyableAddress = ({ address }: { address?: string }) => { + const [copied, setCopied] = useState(false); + + const onCopy = () => { + if (!address) return; + setCopied(true); + navigator.clipboard.writeText(address); + setTimeout(() => setCopied(false), 500); + }; + + return address ? ( + + {truncateAddress(address)} + + + ) : ( + '-' + ); +}; + const Styled: Record = {}; Styled.Table = styled(Table)` @@ -182,3 +209,19 @@ Styled.TimeOutput = styled(Output)` Styled.TxHash = styled(Link)` justify-content: flex-end; `; + +Styled.CopyableAddress = styled(Styled.InlineRow)<{ copied: boolean }>` + cursor: pointer; + + ${({ copied }) => + copied + ? css` + filter: brightness(0.8); + ` + : css` + &:hover { + filter: brightness(1.1); + text-decoration: underline; + } + `} +`; From 64f663ed5bc8ca4ca1ae17868c21b94ca07b1814 Mon Sep 17 00:00:00 2001 From: Aleka Cheung Date: Fri, 25 Aug 2023 14:07:03 -0400 Subject: [PATCH 7/8] add CopyButton component --- src/components/CopyButton.stories.tsx | 28 ++++++++++ src/components/CopyButton.tsx | 65 ++++++++++++++++++++++ src/views/dialogs/MnemonicExportDialog.tsx | 16 +----- src/views/dialogs/ReceiveDialog.tsx | 17 +----- src/views/tables/TransferHistoryTable.tsx | 62 +++++---------------- 5 files changed, 112 insertions(+), 76 deletions(-) create mode 100644 src/components/CopyButton.stories.tsx create mode 100644 src/components/CopyButton.tsx diff --git a/src/components/CopyButton.stories.tsx b/src/components/CopyButton.stories.tsx new file mode 100644 index 0000000..71f4070 --- /dev/null +++ b/src/components/CopyButton.stories.tsx @@ -0,0 +1,28 @@ +import type { Story } from '@ladle/react'; + +import { CopyButton, type CopyButtonProps } from '@/components/CopyButton'; + +import { StoryWrapper } from '.ladle/components'; + +export const CopyButtonStory: Story = (args) => ( + + + +); + +CopyButtonStory.args = { + value: 'some text to copy', +}; + +CopyButtonStory.argTypes = { + shownAsText: { + options: [true, false], + control: { type: 'select' }, + defaultValue: false, + }, + children: { + options: ['some text to copy'], + control: { type: 'select' }, + defaultValue: undefined, + } +}; diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx new file mode 100644 index 0000000..5da154e --- /dev/null +++ b/src/components/CopyButton.tsx @@ -0,0 +1,65 @@ +import { useState } from 'react'; +import styled, { css, type AnyStyledComponent } from 'styled-components'; + +import { ButtonAction } from '@/constants/buttons'; +import { STRING_KEYS } from '@/constants/localization'; + +import { useStringGetter } from '@/hooks'; +import { layoutMixins } from '@/styles/layoutMixins'; + +import { Button, ButtonProps } from './Button'; +import { Icon, IconName } from './Icon'; + +export type CopyButtonProps = { + value?: string; + shownAsText?: boolean; + children?: React.ReactNode; +} & ButtonProps; + +export const CopyButton = ({ value, shownAsText, children, ...buttonProps }: CopyButtonProps) => { + const stringGetter = useStringGetter(); + const [copied, setCopied] = useState(false); + + const onCopy = () => { + if (!value) return; + + setCopied(true); + navigator.clipboard.writeText(value); + setTimeout(() => setCopied(false), 500); + }; + + return shownAsText ? ( + + {children} + + + ) : ( + + ); +}; + +const Styled: Record = {}; + +Styled.InlineRow = styled.div<{ copied: boolean }>` + ${layoutMixins.inlineRow} + cursor: pointer; + + ${({ copied }) => + copied + ? css` + filter: brightness(0.8); + ` + : css` + &:hover { + filter: brightness(1.1); + text-decoration: underline; + } + `} +`; diff --git a/src/views/dialogs/MnemonicExportDialog.tsx b/src/views/dialogs/MnemonicExportDialog.tsx index b654170..a53c7e0 100644 --- a/src/views/dialogs/MnemonicExportDialog.tsx +++ b/src/views/dialogs/MnemonicExportDialog.tsx @@ -9,7 +9,7 @@ import { breakpoints } from '@/styles'; import { layoutMixins } from '@/styles/layoutMixins'; import { AlertMessage } from '@/components/AlertMessage'; -import { Button } from '@/components/Button'; +import { CopyButton } from '@/components/CopyButton'; import { Dialog } from '@/components/Dialog'; import { Checkbox } from '@/components/Checkbox'; import { Icon, IconName } from '@/components/Icon'; @@ -30,21 +30,12 @@ export const MnemonicExportDialog = ({ setIsOpen }: ElementProps) => { const [hasAcknowledged, setHasAcknowledged] = useState(false); const [currentStep, setCurrentStep] = useState(MnemonicExportStep.AcknowledgeRisk); const [isShowing, setIsShowing] = useState(false); - const [copied, setCopied] = useState(false); const stringGetter = useStringGetter(); const { hdKey } = useAccounts(); const { mnemonic } = hdKey ?? {}; - const onCopy = () => { - setCopied(true); - if (mnemonic) { - navigator.clipboard.writeText(mnemonic); - } - setTimeout(() => setCopied(false), 500); - }; - const title = { [MnemonicExportStep.AcknowledgeRisk]: stringGetter({ key: STRING_KEYS.REVEAL_SECRET_PHRASE }), [MnemonicExportStep.DisplayMnemonic]: stringGetter({ key: STRING_KEYS.EXPORT_SECRET_PHRASE }), @@ -116,10 +107,7 @@ export const MnemonicExportDialog = ({ setIsOpen }: ElementProps) => { } > - + ), diff --git a/src/views/dialogs/ReceiveDialog.tsx b/src/views/dialogs/ReceiveDialog.tsx index 0f8db5e..b9546fa 100644 --- a/src/views/dialogs/ReceiveDialog.tsx +++ b/src/views/dialogs/ReceiveDialog.tsx @@ -1,7 +1,6 @@ import { useState } from 'react'; import styled, { type AnyStyledComponent } from 'styled-components'; -import { ButtonAction } from '@/constants/buttons'; import { STRING_KEYS } from '@/constants/localization'; import { DydxChainAsset } from '@/constants/wallets'; @@ -10,9 +9,8 @@ import { layoutMixins } from '@/styles/layoutMixins'; import { useAccounts, useStringGetter } from '@/hooks'; import { AssetIcon } from '@/components/AssetIcon'; -import { Button } from '@/components/Button'; +import { CopyButton } from '@/components/CopyButton'; import { Dialog } from '@/components/Dialog'; -import { Icon, IconName } from '@/components/Icon'; import { QrCode } from '@/components/QrCode'; import { SelectItem, SelectMenu } from '@/components/SelectMenu'; import { WithDetailsReceipt } from '@/components/WithDetailsReceipt'; @@ -31,14 +29,6 @@ export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.DYDX, setIsOpen } const { dydxAddress } = useAccounts(); const [asset, setAsset] = useState(selectedAsset); - const [copied, setCopied] = useState(false); - - const onCopy = () => { - if (!dydxAddress) return; - setCopied(true); - navigator.clipboard.writeText(dydxAddress); - setTimeout(() => setCopied(false), 500); - }; const assetOptions = [ { @@ -89,10 +79,7 @@ export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.DYDX, setIsOpen } > - + )} diff --git a/src/views/tables/TransferHistoryTable.tsx b/src/views/tables/TransferHistoryTable.tsx index 59fa60b..cf7c9c3 100644 --- a/src/views/tables/TransferHistoryTable.tsx +++ b/src/views/tables/TransferHistoryTable.tsx @@ -1,9 +1,10 @@ -import { useState } from 'react'; import styled, { type AnyStyledComponent, css } from 'styled-components'; import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import type { ColumnSize } from '@react-types/table'; import { type SubaccountTransfer } from '@/constants/abacus'; +import { ButtonAction } from '@/constants/buttons'; +import { DialogTypes } from '@/constants/dialogs'; import { STRING_KEYS, StringGetterFunction } from '@/constants/localization'; import { useBreakpoints, useStringGetter } from '@/hooks'; @@ -11,20 +12,19 @@ import { useBreakpoints, useStringGetter } from '@/hooks'; import { layoutMixins } from '@/styles/layoutMixins'; import { tradeViewMixins } from '@/styles/tradeViewMixins'; -import { Icon, IconName } from '@/components/Icon'; +import { Button } from '@/components/Button'; +import { CopyButton } from '@/components/CopyButton'; +import { Icon } from '@/components/Icon'; +import { Link } from '@/components/Link'; import { Output, OutputType } from '@/components/Output'; import { Table, TableCell, TableColumnHeader, type ColumnDef } from '@/components/Table'; +import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton'; import { getSubaccountTransfers } from '@/state/accountSelectors'; +import { calculateCanAccountTrade } from '@/state/accountCalculators'; +import { openDialog } from '@/state/dialogs'; import { truncateAddress } from '@/lib/wallet'; -import { Button } from '@/components/Button'; -import { ButtonAction } from '@/constants/buttons'; -import { openDialog } from '@/state/dialogs'; -import { DialogTypes } from '@/constants/dialogs'; -import { Link } from '@/components/Link'; -import { calculateCanAccountTrade } from '@/state/accountCalculators'; -import { OnboardingTriggerButton } from '../dialogs/OnboardingTriggerButton'; const MOBILE_TRANSFERS_PER_PAGE = 50; @@ -79,8 +79,12 @@ const getTransferHistoryTableColumnDef = ({ ), renderCell: ({ fromAddress, toAddress }) => ( - - + + {fromAddress ? truncateAddress(fromAddress) : '-'} + {' '} + + {toAddress ? truncateAddress(toAddress) : '-'} + ), }, @@ -168,26 +172,6 @@ export const TransferHistoryTable = ({ ); }; -const CopyableAddress = ({ address }: { address?: string }) => { - const [copied, setCopied] = useState(false); - - const onCopy = () => { - if (!address) return; - setCopied(true); - navigator.clipboard.writeText(address); - setTimeout(() => setCopied(false), 500); - }; - - return address ? ( - - {truncateAddress(address)} - - - ) : ( - '-' - ); -}; - const Styled: Record = {}; Styled.Table = styled(Table)` @@ -209,19 +193,3 @@ Styled.TimeOutput = styled(Output)` Styled.TxHash = styled(Link)` justify-content: flex-end; `; - -Styled.CopyableAddress = styled(Styled.InlineRow)<{ copied: boolean }>` - cursor: pointer; - - ${({ copied }) => - copied - ? css` - filter: brightness(0.8); - ` - : css` - &:hover { - filter: brightness(1.1); - text-decoration: underline; - } - `} -`; From 195ddd5af77708fee4b0e61099137ee832da6bd0 Mon Sep 17 00:00:00 2001 From: jaredvu Date: Mon, 28 Aug 2023 08:43:36 -0700 Subject: [PATCH 8/8] Move localeProvider, add label, valid marketId --- src/App.tsx | 28 +++++++++-------- src/constants/routes.ts | 4 ++- src/hooks/useCurrentMarketId.ts | 30 ++++++++++--------- src/main.tsx | 7 ++--- .../forms/TradeForm/AdvancedTradeOptions.tsx | 1 + 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1190db9..1869616 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,10 +10,10 @@ import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes'; import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks'; import { DydxProvider } from '@/hooks/useDydxClient'; import { AccountsProvider } from '@/hooks/useAccounts'; -import { SubaccountProvider } from './hooks/useSubaccount'; import { DialogAreaProvider, useDialogArea } from './hooks/useDialogArea'; +import { LocaleProvider } from './hooks/useLocaleSeparators'; import { NotificationsProvider } from './hooks/useNotifications'; - +import { SubaccountProvider } from './hooks/useSubaccount'; import { GuardedMobileRoute } from '@/components/GuardedMobileRoute'; import MarketsPage from '@/pages/markets/Markets'; @@ -100,17 +100,19 @@ const App = () => ( - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/constants/routes.ts b/src/constants/routes.ts index bd8642a..2017a83 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -1,3 +1,5 @@ +import { DEFAULT_MARKETID } from './markets'; + export enum AppRoute { Markets = '/markets', Portfolio = '/portfolio', @@ -31,7 +33,7 @@ export enum MobileSettingsRoute { export const TRADE_ROUTE = `${AppRoute.Trade}/:market`; export const PORTFOLIO_ROUTE = `${AppRoute.Portfolio}/:subroute`; export const HISTORY_ROUTE = `${AppRoute.Portfolio}/${PortfolioRoute.History}/:subroute`; -export const DEFAULT_TRADE_ROUTE = `${AppRoute.Trade}/ETH-USD`; +export const DEFAULT_TRADE_ROUTE = `${AppRoute.Trade}/${DEFAULT_MARKETID}`; export const SETTINGS_ROUTE = `${AppRoute.Settings}/*`; export const DEFAULT_DOCUMENT_TITLE = 'dYdX'; diff --git a/src/hooks/useCurrentMarketId.ts b/src/hooks/useCurrentMarketId.ts index 837a860..528364b 100644 --- a/src/hooks/useCurrentMarketId.ts +++ b/src/hooks/useCurrentMarketId.ts @@ -1,5 +1,5 @@ -import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useEffect, useMemo } from 'react'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { useMatch, useNavigate } from 'react-router-dom'; import { LocalStorageKey } from '@/constants/localStorage'; @@ -13,6 +13,7 @@ import { setCurrentMarketId } from '@/state/perpetuals'; import abacusStateManager from '@/lib/abacus'; import { useLocalStorage } from './useLocalStorage'; +import { getMarketIds } from '@/state/perpetualsSelectors'; export const useCurrentMarketId = () => { const navigate = useNavigate(); @@ -20,26 +21,27 @@ export const useCurrentMarketId = () => { const { marketId } = match?.params ?? {}; const dispatch = useDispatch(); const selectedNetwork = useSelector(getSelectedNetwork); + const marketIds = useSelector(getMarketIds, shallowEqual); const [lastViewedMarket, setLastViewedMarket] = useLocalStorage({ key: LocalStorageKey.LastViewedMarket, defaultValue: DEFAULT_MARKETID, }); + const validId = useMemo(() => { + if (marketIds.length === 0) return marketId ?? lastViewedMarket; + if (!marketIds.includes(marketId)) return DEFAULT_MARKETID; + return marketId ?? lastViewedMarket; + }, [marketIds, marketId]); + useEffect(() => { - setLastViewedMarket(marketId ?? DEFAULT_MARKETID); - dispatch(setCurrentMarketId(marketId ?? DEFAULT_MARKETID)); + setLastViewedMarket(validId); + dispatch(setCurrentMarketId(validId)); dispatch(closeDialogInTradeBox()); - if (!marketId) { - navigate(lastViewedMarket ? `${AppRoute.Trade}/${lastViewedMarket}` : DEFAULT_TRADE_ROUTE, { - replace: true, - }); - } else { - navigate(`${AppRoute.Trade}/${marketId}`, { - replace: true, - }); - } - }, [marketId]); + navigate(`${AppRoute.Trade}/${validId}`, { + replace: true, + }); + }, [validId]); useEffect(() => { abacusStateManager.setMarket(marketId ?? DEFAULT_MARKETID); diff --git a/src/main.tsx b/src/main.tsx index 2a29686..e8a6619 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,5 @@ import './polyfills'; -import { Fragment, StrictMode } from 'react'; +import { StrictMode } from 'react'; import ReactDOM from 'react-dom/client'; import { HashRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; @@ -11,15 +11,12 @@ import { ErrorBoundary } from './components/ErrorBoundary'; import './index.css'; import App from './App'; -import { LocaleProvider } from './hooks/useLocaleSeparators'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - } /> - + } /> diff --git a/src/views/forms/TradeForm/AdvancedTradeOptions.tsx b/src/views/forms/TradeForm/AdvancedTradeOptions.tsx index 477f947..5fa2ef3 100644 --- a/src/views/forms/TradeForm/AdvancedTradeOptions.tsx +++ b/src/views/forms/TradeForm/AdvancedTradeOptions.tsx @@ -109,6 +109,7 @@ export const AdvancedTradeOptions = () => { {executionOptions && ( abacusStateManager.setTradeValue({ value: selectedTimeInForceOption,