From e15f4b919cc3f3dddc5995bd783deb55a905cfe4 Mon Sep 17 00:00:00 2001 From: jaredvu Date: Fri, 25 Aug 2023 10:59:35 -0700 Subject: [PATCH] useCurrentMarketId hook for navigation --- src/App.tsx | 5 +-- src/hooks/index.ts | 2 + src/hooks/useCurrentMarketId.ts | 47 ++++++++++++++++++++++ src/hooks/usePageTitlePriceUpdates.ts | 2 +- src/layout/Footer/FooterMobile.tsx | 4 +- src/pages/trade/MarketSelectorAndStats.tsx | 10 ++--- src/pages/trade/Trade.tsx | 19 +++------ src/pages/trade/VerticalPanel.tsx | 4 +- src/state/perpetuals.ts | 17 +++++++- src/state/perpetualsSelectors.ts | 3 +- src/views/MarketsDropdown.tsx | 4 +- 11 files changed, 83 insertions(+), 34 deletions(-) create mode 100644 src/hooks/useCurrentMarketId.ts diff --git a/src/App.tsx b/src/App.tsx index e1ccfe3..1190db9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -61,10 +61,7 @@ const Content = () => { } /> - } - /> + } /> } /> {import.meta.env.MODE !== 'production' && ( diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 12cfedd..bc0c3cd 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,6 +1,7 @@ import { useApiState } from './useApiState'; import { useBreakpoints } from './useBreakpoints'; import { useCommandMenu } from './useCommandMenu'; +import { useCurrentMarketId } from './useCurrentMarketId'; import { useInterval } from './useInterval'; import { useDocumentTitle } from './useDocumentTitle'; import { useDydxClient } from './useDydxClient'; @@ -23,6 +24,7 @@ export { useApiState, useBreakpoints, useCommandMenu, + useCurrentMarketId, useDocumentTitle, useDydxClient, useAccountBalance, diff --git a/src/hooks/useCurrentMarketId.ts b/src/hooks/useCurrentMarketId.ts new file mode 100644 index 0000000..837a860 --- /dev/null +++ b/src/hooks/useCurrentMarketId.ts @@ -0,0 +1,47 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useMatch, useNavigate } from 'react-router-dom'; + +import { LocalStorageKey } from '@/constants/localStorage'; +import { DEFAULT_MARKETID } from '@/constants/markets'; +import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes'; + +import { getSelectedNetwork } from '@/state/appSelectors'; +import { closeDialogInTradeBox } from '@/state/dialogs'; +import { setCurrentMarketId } from '@/state/perpetuals'; + +import abacusStateManager from '@/lib/abacus'; + +import { useLocalStorage } from './useLocalStorage'; + +export const useCurrentMarketId = () => { + const navigate = useNavigate(); + const match = useMatch(`/${AppRoute.Trade}/:marketId`); + const { marketId } = match?.params ?? {}; + const dispatch = useDispatch(); + const selectedNetwork = useSelector(getSelectedNetwork); + const [lastViewedMarket, setLastViewedMarket] = useLocalStorage({ + key: LocalStorageKey.LastViewedMarket, + defaultValue: DEFAULT_MARKETID, + }); + + useEffect(() => { + setLastViewedMarket(marketId ?? DEFAULT_MARKETID); + dispatch(setCurrentMarketId(marketId ?? DEFAULT_MARKETID)); + dispatch(closeDialogInTradeBox()); + + if (!marketId) { + navigate(lastViewedMarket ? `${AppRoute.Trade}/${lastViewedMarket}` : DEFAULT_TRADE_ROUTE, { + replace: true, + }); + } else { + navigate(`${AppRoute.Trade}/${marketId}`, { + replace: true, + }); + } + }, [marketId]); + + useEffect(() => { + abacusStateManager.setMarket(marketId ?? DEFAULT_MARKETID); + }, [selectedNetwork, marketId]); +}; diff --git a/src/hooks/usePageTitlePriceUpdates.ts b/src/hooks/usePageTitlePriceUpdates.ts index c8c3f3e..3093974 100644 --- a/src/hooks/usePageTitlePriceUpdates.ts +++ b/src/hooks/usePageTitlePriceUpdates.ts @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { shallowEqual, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { DEFAULT_DOCUMENT_TITLE } from '@/constants/routes'; diff --git a/src/layout/Footer/FooterMobile.tsx b/src/layout/Footer/FooterMobile.tsx index 6b7b9ef..5cac678 100644 --- a/src/layout/Footer/FooterMobile.tsx +++ b/src/layout/Footer/FooterMobile.tsx @@ -1,6 +1,7 @@ import styled, { type AnyStyledComponent } from 'styled-components'; import { useDispatch, useSelector } from 'react-redux'; +import { ButtonAction, ButtonSize } from '@/constants/buttons'; import { DialogTypes } from '@/constants/dialogs'; import { STRING_KEYS } from '@/constants/localization'; import { DEFAULT_MARKETID } from '@/constants/markets'; @@ -15,9 +16,8 @@ import { BellIcon, MarketsIcon, PlayIcon, PortfolioIcon, ProfileIcon, TradeIcon import { IconButton } from '@/components/IconButton'; import { calculateCanAccountTrade } from '@/state/accountCalculators'; -import { getCurrentMarketId } from '@/state/perpetualsSelectors'; import { openDialog } from '@/state/dialogs'; -import { ButtonAction, ButtonSize } from '@/constants/buttons'; +import { getCurrentMarketId } from '@/state/perpetualsSelectors'; export const FooterMobile = () => { const dispatch = useDispatch(); diff --git a/src/pages/trade/MarketSelectorAndStats.tsx b/src/pages/trade/MarketSelectorAndStats.tsx index 077a402..73138d1 100644 --- a/src/pages/trade/MarketSelectorAndStats.tsx +++ b/src/pages/trade/MarketSelectorAndStats.tsx @@ -1,21 +1,19 @@ import styled, { type AnyStyledComponent } from 'styled-components'; import { shallowEqual, useSelector } from 'react-redux'; -import { DEFAULT_MARKETID } from '@/constants/markets'; import { layoutMixins } from '@/styles/layoutMixins'; import { VerticalSeparator } from '@/components/Separator'; -import { getCurrentMarketAssetData } from '@/state/assetsSelectors'; - -import { getCurrentMarketId } from '@/state/perpetualsSelectors'; - import { MarketsDropdown } from '@/views/MarketsDropdown'; import { MarketStatsDetails } from '@/views/MarketStatsDetails'; +import { getCurrentMarketAssetData } from '@/state/assetsSelectors'; +import { getCurrentMarketId } from '@/state/perpetualsSelectors'; + export const MarketSelectorAndStats = ({ className }: { className?: string }) => { const { symbol = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {}; - const currentMarketId = useSelector(getCurrentMarketId) ?? DEFAULT_MARKETID; + const currentMarketId = useSelector(getCurrentMarketId); return ( diff --git a/src/pages/trade/Trade.tsx b/src/pages/trade/Trade.tsx index 5035bcd..1f97b66 100644 --- a/src/pages/trade/Trade.tsx +++ b/src/pages/trade/Trade.tsx @@ -1,16 +1,13 @@ -import { useEffect, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import styled, { AnyStyledComponent, css } from 'styled-components'; -import { useLocation } from 'react-router-dom'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { breakpoints } from '@/styles'; import { layoutMixins } from '@/styles/layoutMixins'; import { TradeLayouts } from '@/constants/layout'; -import { useBreakpoints, usePageTitlePriceUpdates } from '@/hooks'; - -import { setTradeLocation } from '@/state/navigation'; +import { useBreakpoints, useCurrentMarketId, usePageTitlePriceUpdates } from '@/hooks'; import { calculateCanAccountTrade } from '@/state/accountCalculators'; import { getSelectedTradeLayout } from '@/state/layoutSelectors'; @@ -32,18 +29,12 @@ import { TradeBox } from '@/views/TradeBox'; const TradePage = () => { const tradePageRef = useRef(null); - const location = useLocation(); - const dispatch = useDispatch(); + useCurrentMarketId(); const { isTablet } = useBreakpoints(); const tradeLayout = useSelector(getSelectedTradeLayout); - - const [isHorizontalPanelOpen, setIsHorizontalPanelOpen] = useState(true); - const canAccountTrade = useSelector(calculateCanAccountTrade); - useEffect(() => { - dispatch(setTradeLocation(location)); - }, [location.pathname]); + const [isHorizontalPanelOpen, setIsHorizontalPanelOpen] = useState(true); usePageTitlePriceUpdates(); diff --git a/src/pages/trade/VerticalPanel.tsx b/src/pages/trade/VerticalPanel.tsx index d180f81..d35848c 100644 --- a/src/pages/trade/VerticalPanel.tsx +++ b/src/pages/trade/VerticalPanel.tsx @@ -7,13 +7,13 @@ import { STRING_KEYS } from '@/constants/localization'; import { useStringGetter } from '@/hooks'; -import { getCurrentMarketId } from '@/state/perpetualsSelectors'; - import { Tabs } from '@/components/Tabs'; import { Orderbook, orderbookMixins, OrderbookScrollBehavior } from '@/views/tables/Orderbook'; import { LiveTrades } from '@/views/tables/LiveTrades'; +import { getCurrentMarketId } from '@/state/perpetualsSelectors'; + enum Tab { Orderbook = 'Orderbook', Trades = 'Trades', diff --git a/src/state/perpetuals.ts b/src/state/perpetuals.ts index 629103c..848b3d8 100644 --- a/src/state/perpetuals.ts +++ b/src/state/perpetuals.ts @@ -10,6 +10,10 @@ import type { } from '@/constants/abacus'; import { Candle, RESOLUTION_MAP } from '@/constants/candles'; +import { LocalStorageKey } from '@/constants/localStorage'; +import { DEFAULT_MARKETID } from '@/constants/markets'; + +import { getLocalStorage } from '@/lib/localStorage'; interface CandleDataByMarket { data: Record; @@ -17,6 +21,7 @@ interface CandleDataByMarket { } export interface PerpetualsState { + currentMarketId?: string; candles: Record; liveTrades?: Record; markets?: Record; @@ -25,6 +30,7 @@ export interface PerpetualsState { } const initialState: PerpetualsState = { + currentMarketId: undefined, candles: {}, liveTrades: {}, markets: undefined, @@ -38,6 +44,9 @@ export const perpetualsSlice = createSlice({ name: 'Perpetuals', initialState, reducers: { + setCurrentMarketId: (state: PerpetualsState, action: PayloadAction) => { + state.currentMarketId = action.payload; + }, setCandles: ( state: PerpetualsState, action: PayloadAction<{ candles: Candle[]; marketId: string; resolution: string }> @@ -116,11 +125,17 @@ export const perpetualsSlice = createSlice({ ) => { state.historicalFundings[action.payload.marketId] = action.payload.historicalFundings; }, - resetPerpetualsState: () => initialState, + resetPerpetualsState: () => + ({ + ...initialState, + currentMarketId: + getLocalStorage({ key: LocalStorageKey.LastViewedMarket }) ?? DEFAULT_MARKETID, + } as PerpetualsState), }, }); export const { + setCurrentMarketId, setCandles, setLiveTrades, setMarkets, diff --git a/src/state/perpetualsSelectors.ts b/src/state/perpetualsSelectors.ts index ee0f353..17a6d33 100644 --- a/src/state/perpetualsSelectors.ts +++ b/src/state/perpetualsSelectors.ts @@ -11,8 +11,7 @@ import { mapCandle } from '@/lib/tradingView/utils'; * @param state * @returns marketId of the market the user is currently viewing */ -export const getCurrentMarketId = (state: RootState) => - matchPath(TRADE_ROUTE, state.navigation.tradeLocation.pathname)?.params.market; +export const getCurrentMarketId = (state: RootState) => state.perpetuals.currentMarketId; /** * @returns assetId of the currentMarket diff --git a/src/views/MarketsDropdown.tsx b/src/views/MarketsDropdown.tsx index af46c44..ea5d0b0 100644 --- a/src/views/MarketsDropdown.tsx +++ b/src/views/MarketsDropdown.tsx @@ -15,7 +15,7 @@ import { AssetIcon } from '@/components/AssetIcon'; import { Icon, IconName } from '@/components/Icon'; import { Output, OutputType } from '@/components/Output'; import { Popover, TriggerType } from '@/components/Popover'; -import { Tag, TagSize } from '@/components/Tag'; +import { Tag } from '@/components/Tag'; import { Toolbar } from '@/components/Toolbar'; import { ColumnDef, Table } from '@/components/Table'; @@ -152,7 +152,7 @@ const MarketsDropdownContent = ({ onRowAction }: { onRowAction?: (market: string ); }; -export const MarketsDropdown: React.FC<{ currentMarketId: string; symbol: string | null }> = memo( +export const MarketsDropdown: React.FC<{ currentMarketId?: string; symbol: string | null }> = memo( ({ currentMarketId, symbol = '' }) => { const [isOpen, setIsOpen] = useState(false); const stringGetter = useStringGetter();