diff --git a/apps/ledger-live-mobile/src/actions/settings.js b/apps/ledger-live-mobile/src/actions/settings.js index c7afa0231a4c..c441b54e9f4e 100755 --- a/apps/ledger-live-mobile/src/actions/settings.js +++ b/apps/ledger-live-mobile/src/actions/settings.js @@ -6,6 +6,7 @@ import type { Currency } from "@ledgerhq/live-common/lib/types"; import type { DeviceModelInfo } from "@ledgerhq/live-common/lib/types/manager"; import type { Device } from "@ledgerhq/live-common/lib/hw/actions/types"; import type { PortfolioRange } from "@ledgerhq/live-common/lib/portfolio/v2/types"; +import { MarketListRequestParams } from "@ledgerhq/live-common/lib/market/types"; import { selectedTimeRangeSelector } from "../reducers/settings"; export type CurrencySettings = { @@ -219,6 +220,23 @@ export const setLastConnectedDevice = (device: Device) => ({ payload: device, }); +export const setMarketRequestParams = ( + marketRequestParams: MarketListRequestParams, +) => ({ + type: "SET_MARKET_REQUEST_PARAMS", + payload: marketRequestParams, +}); + +export const setMarketCounterCurrency = (currency: string) => ({ + type: "SET_MARKET_COUNTER_CURRENCY", + payload: currency, +}); + +export const setMarketFilterByStarredAccounts = (payload: boolean) => ({ + type: "SET_MARKET_FILTER_BY_STARRED_ACCOUNTS", + payload, +}); + type PortfolioRangeOption = { key: PortfolioRange, value: string, diff --git a/apps/ledger-live-mobile/src/reducers/settings.js b/apps/ledger-live-mobile/src/reducers/settings.js index c597abdc89af..0c1909d5b19b 100755 --- a/apps/ledger-live-mobile/src/reducers/settings.js +++ b/apps/ledger-live-mobile/src/reducers/settings.js @@ -20,6 +20,7 @@ import { getAccountCurrency } from "@ledgerhq/live-common/lib/account/helpers"; import Config from "react-native-config"; import type { PortfolioRange } from "@ledgerhq/live-common/lib/portfolio/v2/types"; import type { DeviceModelInfo } from "@ledgerhq/live-common/lib/types/manager"; +import { MarketListRequestParams } from "@ledgerhq/live-common/lib/market/types"; import { currencySettingsDefaults } from "../helpers/CurrencySettingsDefaults"; import type { State } from "."; import { SLIDES } from "../components/Carousel/shared"; @@ -99,6 +100,9 @@ export type SettingsState = { lastSeenDevice: ?DeviceModelInfo, starredMarketCoins: string[], lastConnectedDevice: ?Device, + marketRequestParams: MarketListRequestParams, + marketCounterCurrency: ?string, + marketFilterByStarredAccounts: boolean, }; export const INITIAL_STATE: SettingsState = { @@ -141,6 +145,16 @@ export const INITIAL_STATE: SettingsState = { lastSeenDevice: null, starredMarketCoins: [], lastConnectedDevice: null, + marketRequestParams: { + range: "24h", + orderBy: "market_cap", + order: "desc", + liveCompatible: false, + sparkline: false, + top100: false, + }, + marketCounterCurrency: null, + marketFilterByStarredAccounts: false, }; const pairHash = (from, to) => `${from.ticker}_${to.ticker}`; @@ -397,6 +411,24 @@ const handlers: Object = { ...state, lastConnectedDevice, }), + SET_MARKET_REQUEST_PARAMS: (state: SettingsState, { payload }) => ({ + ...state, + marketRequestParams: { + ...state.marketRequestParams, + ...payload, + }, + }), + SET_MARKET_COUNTER_CURRENCY: (state: SettingsState, { payload }) => ({ + ...state, + marketCounterCurrency: payload, + }), + SET_MARKET_FILTER_BY_STARRED_ACCOUNTS: ( + state: SettingsState, + { payload }, + ) => ({ + ...state, + marketFilterByStarredAccounts: payload, + }), }; const storeSelector = (state: *): SettingsState => state.settings; @@ -587,3 +619,12 @@ export const starredMarketCoinsSelector = (state: State) => export const lastConnectedDeviceSelector = (state: State) => state.settings.lastConnectedDevice; + +export const marketRequestParamsSelector = (state: State) => + state.settings.marketRequestParams; + +export const marketCounterCurrencySelector = (state: State) => + state.settings.marketCounterCurrency; + +export const marketFilterByStarredAccountsSelector = (state: State) => + state.settings.marketFilterByStarredAccounts; diff --git a/apps/ledger-live-mobile/src/screens/Market/MarketCurrencySelect.tsx b/apps/ledger-live-mobile/src/screens/Market/MarketCurrencySelect.tsx index 7beb8fd174e2..5aa1e114b6ef 100644 --- a/apps/ledger-live-mobile/src/screens/Market/MarketCurrencySelect.tsx +++ b/apps/ledger-live-mobile/src/screens/Market/MarketCurrencySelect.tsx @@ -5,8 +5,13 @@ import React, { useCallback, memo, useState, useRef, useEffect } from "react"; import { Trans, useTranslation } from "react-i18next"; import { FlatList, TouchableOpacity, Image } from "react-native"; import styled, { useTheme } from "styled-components/native"; +import { useDispatch } from "react-redux"; import Search from "../../components/Search"; import { supportedCountervalues } from "../../reducers/settings"; +import { + setMarketCounterCurrency, + setMarketRequestParams, +} from "../../actions/settings"; const RenderEmptyList = ({ theme, @@ -53,6 +58,7 @@ const CheckIconContainer = styled(Flex).attrs({ function MarketCurrencySelect({ navigation }: { navigation: any }) { const { t } = useTranslation(); + const dispatch = useDispatch(); const { colors } = useTheme(); const { counterCurrency, @@ -78,6 +84,7 @@ function MarketCurrencySelect({ navigation }: { navigation: any }) { const onSelectCurrency = useCallback( (value: string) => { + dispatch(setMarketCounterCurrency(value)); setCounterCurrency(value); navigation.goBack(); }, diff --git a/apps/ledger-live-mobile/src/screens/Market/MarketDataProviderWrapper.tsx b/apps/ledger-live-mobile/src/screens/Market/MarketDataProviderWrapper.tsx index 6165a5305221..86a3de6c9a08 100644 --- a/apps/ledger-live-mobile/src/screens/Market/MarketDataProviderWrapper.tsx +++ b/apps/ledger-live-mobile/src/screens/Market/MarketDataProviderWrapper.tsx @@ -5,7 +5,13 @@ import apiMock from "@ledgerhq/live-common/lib/market/api/api.mock"; import Config from "react-native-config"; import { MarketDataProvider } from "@ledgerhq/live-common/lib/market/MarketDataProvider"; import { useNetInfo } from "@react-native-community/netinfo"; -import { counterValueCurrencySelector } from "../../reducers/settings"; +import { + counterValueCurrencySelector, + marketCounterCurrencySelector, + marketFilterByStarredAccountsSelector, + marketRequestParamsSelector, + starredMarketCoinsSelector, +} from "../../reducers/settings"; type Props = { children: React.ReactNode; @@ -15,30 +21,40 @@ export default function MarketDataProviderWrapper({ children, }: Props): ReactElement { const counterValueCurrency: any = useSelector(counterValueCurrencySelector); + const marketRequestParams: any = useSelector(marketRequestParamsSelector); + const marketCounterCurrency: any = useSelector(marketCounterCurrencySelector); + const starredMarketCoins: string[] = useSelector(starredMarketCoinsSelector); + const filterByStarredAccount: boolean = useSelector( + marketFilterByStarredAccountsSelector, + ); const { isConnected } = useNetInfo(); + const counterCurrency = !isConnected + ? undefined // without coutervalues service is not initialized with cg data, this forces it to fetch it at least once the network is on + : marketCounterCurrency // If there is a stored market counter currency we use it, otherwise we use the setting countervalue currency + ? { ticker: marketCounterCurrency } + : counterValueCurrency + ? // @TODO move this toLowercase check on live-common + { ticker: counterValueCurrency.ticker?.toLowerCase() } + : counterValueCurrency; + return ( diff --git a/apps/ledger-live-mobile/src/screens/Market/index.tsx b/apps/ledger-live-mobile/src/screens/Market/index.tsx index 20ce78c04b5b..ce6492d2cf13 100644 --- a/apps/ledger-live-mobile/src/screens/Market/index.tsx +++ b/apps/ledger-live-mobile/src/screens/Market/index.tsx @@ -1,7 +1,13 @@ /* eslint-disable import/named */ /* eslint-disable import/no-unresolved */ -import React, { useMemo, useCallback, useState, useEffect } from "react"; +import React, { + useMemo, + useCallback, + useState, + useEffect, + useRef, +} from "react"; import { useTheme } from "styled-components/native"; import { Flex, @@ -13,7 +19,7 @@ import { InfiniteLoader, Icons, } from "@ledgerhq/native-ui"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { Trans, useTranslation } from "react-i18next"; import { useMarketData } from "@ledgerhq/live-common/lib/market/MarketDataProvider"; import { rangeDataTable } from "@ledgerhq/live-common/lib/market/utils/rangeDataTable"; @@ -21,7 +27,11 @@ import { FlatList, RefreshControl, TouchableOpacity } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { MarketListRequestParams } from "@ledgerhq/live-common/lib/market/types"; import { useRoute } from "@react-navigation/native"; -import { starredMarketCoinsSelector } from "../../reducers/settings"; +import { useNetInfo } from "@react-native-community/netinfo"; +import { + marketFilterByStarredAccountsSelector, + starredMarketCoinsSelector, +} from "../../reducers/settings"; import MarketRowItem from "./MarketRowItem"; import { useLocale } from "../../context/Locale"; import SortBadge, { Badge } from "./SortBadge"; @@ -31,7 +41,10 @@ import { track } from "../../analytics"; import TrackScreen from "../../analytics/TrackScreen"; import { useProviders } from "../Swap/SwapEntry"; import Illustration from "../../images/illustration/Illustration"; -import { useNetInfo } from "@react-native-community/netinfo"; +import { + setMarketFilterByStarredAccounts, + setMarketRequestParams, +} from "../../actions/settings"; const noResultIllustration = { dark: require("../../images/illustration/Dark/_051.png"), @@ -59,31 +72,39 @@ function getAnalyticsProperties( const BottomSection = ({ navigation }: { navigation: any }) => { const { t } = useTranslation(); + const dispatch = useDispatch(); const { requestParams, counterCurrency, refresh } = useMarketData(); - const { range, starred = [], orderBy, order, top100 } = requestParams; + const { range, orderBy, order, top100 } = requestParams; const starredMarketCoins: string[] = useSelector(starredMarketCoinsSelector); - const starFilterOn = starred.length > 0; + const filterByStarredAccount: boolean = useSelector( + marketFilterByStarredAccountsSelector, + ); + const firstMount = useRef(true); // To known if this is the first mount of the page useEffect(() => { - if (starFilterOn) { + if (firstMount.current) { + // We don't want to refresh the market data directly on mount, the data is already refreshed with wanted parameters from MarketDataProviderWrapper + firstMount.current = false; + return; + } + if (filterByStarredAccount) { refresh({ starred: starredMarketCoins }); + } else { + refresh({ starred: [], search: "" }); } - }, [refresh, starFilterOn, starredMarketCoins]); + }, [refresh, filterByStarredAccount, starredMarketCoins]); const toggleFilterByStarredAccounts = useCallback(() => { - if (starredMarketCoins.length > 0) { - const starred = starFilterOn ? [] : starredMarketCoins; - if (!starFilterOn) { - track( - "Page Market Favourites", - getAnalyticsProperties(requestParams, { - currencies: starredMarketCoins, - }), - ); - } - refresh({ starred, search: "" }); + if (!filterByStarredAccount) { + track( + "Page Market Favourites", + getAnalyticsProperties(requestParams, { + currencies: starredMarketCoins, + }), + ); } - }, [refresh, starFilterOn, starredMarketCoins, requestParams]); + dispatch(setMarketFilterByStarredAccounts(!filterByStarredAccount)); + }, [dispatch, filterByStarredAccount]); const timeRanges = useMemo( () => @@ -108,9 +129,10 @@ const BottomSection = ({ navigation }: { navigation: any }) => { "Page Market", getAnalyticsProperties({ ...requestParams, ...value }), ); + dispatch(setMarketRequestParams(value)); refresh(value); }, - [refresh, requestParams], + [dispatch, refresh, requestParams], ); const timeRangeValue = timeRanges.find(({ value }) => value === range); @@ -126,11 +148,11 @@ const BottomSection = ({ navigation }: { navigation: any }) => { showsHorizontalScrollIndicator={false} > - {starredMarketCoins.length <= 0 && !starFilterOn ? null : ( + {starredMarketCoins.length <= 0 && !filterByStarredAccount ? null : ( @@ -173,7 +195,7 @@ const BottomSection = ({ navigation }: { navigation: any }) => { order: "asc", orderBy: "market_cap", top100: false, - limit: 20 + limit: 20, }, value: "market_cap_asc", }, @@ -183,7 +205,7 @@ const BottomSection = ({ navigation }: { navigation: any }) => { order: "desc", orderBy: "market_cap", top100: false, - limit: 20 + limit: 20, }, value: "market_cap_desc", }, @@ -282,7 +304,7 @@ export default function Market({ navigation }: { navigation: any }) { ); useEffect(() => { - if (!isConnected) setIsLoading(false); + if (!isConnected) setIsLoading(false); }, [isConnected]); useEffect(() => { @@ -335,61 +357,63 @@ export default function Market({ navigation }: { navigation: any }) { const renderEmptyComponent = useCallback( () => - search ? ( // shows up in case of no search results - - - - - - {t("market.warnings.noCryptosFound")} - - - - - {""} - - - - - - ) : !isConnected ? ( // shows up in case of network down - + + + + + {t("market.warnings.noCryptosFound")} + + + - - - - - {t("errors.NetworkDown.title")} - - - {t("errors.NetworkDown.description")} + + {""} + + + + + ) : !isConnected ? ( // shows up in case of network down + + + - ): , // shows up in case loading is ongoing + + {t("errors.NetworkDown.title")} + + + {t("errors.NetworkDown.description")} + + + ) : ( + + ), // shows up in case loading is ongoing [error, isLoading, resetSearch, search, t], );