From d1cf515cd246b3707ffa88bc6929d217d8b28834 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:38:53 -0600 Subject: [PATCH 01/14] chore: Remove old CeloGoldHistoryChart --- src/account/utils.ts | 3 +- src/alert/reducer.ts | 6 +- src/app/reducers.ts | 2 +- src/app/saga.ts | 14 +- .../CeloNewsFeed.test.tsx | 4 +- src/{exchange => celoNews}/CeloNewsFeed.tsx | 6 +- .../CeloNewsFeedItem.test.tsx | 2 +- .../CeloNewsFeedItem.tsx | 2 +- .../ExchangeHomeScreen.test.tsx | 28 +- .../ExchangeHomeScreen.tsx | 27 +- src/{exchange => celoNews}/types.ts | 0 src/exchange/CeloGoldHistoryChart.test.tsx | 91 ---- src/exchange/CeloGoldHistoryChart.tsx | 308 ------------- .../CeloGoldHistoryChart.test.tsx.snap | 406 ----------------- src/exchange/actions.ts | 22 - src/exchange/reducer.ts | 117 ----- src/firebase/saga.ts | 83 +--- src/navigator/DrawerNavigator.tsx | 2 +- src/redux/migrations.test.ts | 13 +- src/redux/migrations.ts | 14 +- src/redux/reducers.ts | 4 +- src/redux/sagas.ts | 8 +- src/redux/store.test.ts | 11 +- src/redux/store.ts | 2 +- src/statsig/constants.ts | 1 - src/statsig/types.ts | 1 - src/tokens/TokenDetails.test.tsx | 20 - src/tokens/TokenDetails.tsx | 19 +- src/transactions/reducer.ts | 3 +- test/RootStateSchema.json | 411 ++++++++---------- test/schemas.ts | 10 +- 31 files changed, 243 insertions(+), 1397 deletions(-) rename src/{exchange => celoNews}/CeloNewsFeed.test.tsx (98%) rename src/{exchange => celoNews}/CeloNewsFeed.tsx (97%) rename src/{exchange => celoNews}/CeloNewsFeedItem.test.tsx (96%) rename src/{exchange => celoNews}/CeloNewsFeedItem.tsx (98%) rename src/{exchange => celoNews}/ExchangeHomeScreen.test.tsx (69%) rename src/{exchange => celoNews}/ExchangeHomeScreen.tsx (86%) rename src/{exchange => celoNews}/types.ts (100%) delete mode 100644 src/exchange/CeloGoldHistoryChart.test.tsx delete mode 100644 src/exchange/CeloGoldHistoryChart.tsx delete mode 100644 src/exchange/__snapshots__/CeloGoldHistoryChart.test.tsx.snap delete mode 100644 src/exchange/actions.ts delete mode 100644 src/exchange/reducer.ts diff --git a/src/account/utils.ts b/src/account/utils.ts index de47633d408..ca6ce4ec101 100644 --- a/src/account/utils.ts +++ b/src/account/utils.ts @@ -1,5 +1,6 @@ import { parsePhoneNumber } from '@celo/phone-utils' -import { ADDRESS_LENGTH } from 'src/exchange/reducer' + +const ADDRESS_LENGTH = 42 export const isAddressFormat = (content: string): boolean => { return content.startsWith('0x') && content.length === ADDRESS_LENGTH diff --git a/src/alert/reducer.ts b/src/alert/reducer.ts index 663e5313be6..75945b21d8e 100644 --- a/src/alert/reducer.ts +++ b/src/alert/reducer.ts @@ -1,7 +1,6 @@ import { Action } from '@reduxjs/toolkit' import { Actions, ActionTypes, AlertTypes } from 'src/alert/actions' import { ErrorMessages } from 'src/app/ErrorMessages' -import { ActionTypes as ExchangeActionTypes } from 'src/exchange/actions' import { RootState } from 'src/redux/reducers' export enum ErrorDisplayType { @@ -24,10 +23,7 @@ type State = Alert | null const initialState = null -export const reducer = ( - state: State = initialState, - action: ActionTypes | ExchangeActionTypes -): State => { +export const reducer = (state: State = initialState, action: ActionTypes): State => { switch (action.type) { case Actions.SHOW: return { diff --git a/src/app/reducers.ts b/src/app/reducers.ts index 0ff2ab722e8..a03ed0b168a 100644 --- a/src/app/reducers.ts +++ b/src/app/reducers.ts @@ -1,8 +1,8 @@ import { Platform } from 'react-native' import { BIOMETRY_TYPE } from 'react-native-keychain' import { Actions, ActionTypes, AppState, MultichainBetaStatus } from 'src/app/actions' +import { CeloNewsConfig } from 'src/celoNews/types' import { SuperchargeTokenConfigByToken } from 'src/consumerIncentives/types' -import { CeloNewsConfig } from 'src/exchange/types' import { REMOTE_CONFIG_VALUES_DEFAULTS } from 'src/firebase/remoteConfigValuesDefaults' import { Screens } from 'src/navigator/Screens' import { getRehydratePayload, REHYDRATE, RehydrateAction } from 'src/redux/persist-helper' diff --git a/src/app/saga.ts b/src/app/saga.ts index fe04c71f709..1e40fe1cabc 100644 --- a/src/app/saga.ts +++ b/src/app/saga.ts @@ -10,20 +10,20 @@ import { findBestAvailableLanguage } from 'react-native-localize' import { eventChannel } from 'redux-saga' import { e164NumberSelector } from 'src/account/selectors' import { AppEvents, InviteEvents } from 'src/analytics/Events' -import { HooksEnablePreviewOrigin } from 'src/analytics/types' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' +import { HooksEnablePreviewOrigin } from 'src/analytics/types' import { Actions, + OpenDeepLink, + OpenUrlAction, + SetAppState, androidMobileServicesAvailabilityChecked, appLock, inAppReviewRequested, inviteLinkConsumed, minAppVersionDetermined, - OpenDeepLink, openDeepLink, - OpenUrlAction, phoneNumberVerificationMigrated, - SetAppState, setAppState, setSupportedBiometryType, updateRemoteConfigValues, @@ -38,13 +38,13 @@ import { sentryNetworkErrorsSelector, shouldRunVerificationMigrationSelector, } from 'src/app/selectors' +import { CeloNewsConfig } from 'src/celoNews/types' import { DEFAULT_APP_LANGUAGE, FETCH_TIMEOUT_DURATION, isE2EEnv } from 'src/config' import { claimRewardsSuccess } from 'src/consumerIncentives/slice' import { SuperchargeTokenConfigByToken } from 'src/consumerIncentives/types' import { handleDappkitDeepLink } from 'src/dappkit/dappkit' -import { CeloNewsConfig } from 'src/exchange/types' -import { FiatAccountSchemaCountryOverrides } from 'src/fiatconnect/types' import { FiatExchangeFlow } from 'src/fiatExchanges/utils' +import { FiatAccountSchemaCountryOverrides } from 'src/fiatconnect/types' import { appVersionDeprecationChannel, fetchRemoteConfigValues } from 'src/firebase/firebase' import { initI18n } from 'src/i18n' import { @@ -70,9 +70,9 @@ import { getFeatureGate, patchUpdateStatsigUser, setupOverridesFromLaunchArgs } import { StatsigFeatureGates } from 'src/statsig/types' import { swapSuccess } from 'src/swap/slice' import { NetworkId } from 'src/transactions/types' +import Logger from 'src/utils/Logger' import { ensureError } from 'src/utils/ensureError' import { isDeepLink, navigateToURI } from 'src/utils/linking' -import Logger from 'src/utils/Logger' import { safely } from 'src/utils/safely' import { ONE_DAY_IN_MILLIS } from 'src/utils/time' import { isWalletConnectEnabled } from 'src/walletConnect/saga' diff --git a/src/exchange/CeloNewsFeed.test.tsx b/src/celoNews/CeloNewsFeed.test.tsx similarity index 98% rename from src/exchange/CeloNewsFeed.test.tsx rename to src/celoNews/CeloNewsFeed.test.tsx index fff8bca54c0..32bbefad50b 100644 --- a/src/exchange/CeloNewsFeed.test.tsx +++ b/src/celoNews/CeloNewsFeed.test.tsx @@ -4,8 +4,8 @@ import React from 'react' import { Provider } from 'react-redux' import { CeloNewsEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' -import CeloNewsFeed from 'src/exchange/CeloNewsFeed' -import { CeloNewsArticles } from 'src/exchange/types' +import CeloNewsFeed from 'src/celoNews/CeloNewsFeed' +import { CeloNewsArticles } from 'src/celoNews/types' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import networkConfig from 'src/web3/networkConfig' diff --git a/src/exchange/CeloNewsFeed.tsx b/src/celoNews/CeloNewsFeed.tsx similarity index 97% rename from src/exchange/CeloNewsFeed.tsx rename to src/celoNews/CeloNewsFeed.tsx index 1afc91e37f7..bed024aaae4 100644 --- a/src/exchange/CeloNewsFeed.tsx +++ b/src/celoNews/CeloNewsFeed.tsx @@ -5,18 +5,18 @@ import { FlatList, ListRenderItemInfo, StyleSheet, Text, View } from 'react-nati import { CeloNewsEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' import { celoNewsConfigSelector } from 'src/app/selectors' +import CeloNewsFeedItem from 'src/celoNews/CeloNewsFeedItem' +import { CeloNewsArticle, CeloNewsArticles } from 'src/celoNews/types' import Button, { BtnSizes, BtnTypes } from 'src/components/Button' import EmptyView from 'src/components/EmptyView' -import CeloNewsFeedItem from 'src/exchange/CeloNewsFeedItem' -import { CeloNewsArticle, CeloNewsArticles } from 'src/exchange/types' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { useSelector } from 'src/redux/hooks' import colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' -import { fetchWithTimeout } from 'src/utils/fetchWithTimeout' import Logger from 'src/utils/Logger' +import { fetchWithTimeout } from 'src/utils/fetchWithTimeout' import networkConfig from 'src/web3/networkConfig' const TAG = 'exchange/CeloNewsFeed' diff --git a/src/exchange/CeloNewsFeedItem.test.tsx b/src/celoNews/CeloNewsFeedItem.test.tsx similarity index 96% rename from src/exchange/CeloNewsFeedItem.test.tsx rename to src/celoNews/CeloNewsFeedItem.test.tsx index eb1e5a20b65..a9d635d6bab 100644 --- a/src/exchange/CeloNewsFeedItem.test.tsx +++ b/src/celoNews/CeloNewsFeedItem.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, render } from '@testing-library/react-native' import React from 'react' import { CeloNewsEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' -import CeloNewsFeedItem from 'src/exchange/CeloNewsFeedItem' +import CeloNewsFeedItem from 'src/celoNews/CeloNewsFeedItem' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' diff --git a/src/exchange/CeloNewsFeedItem.tsx b/src/celoNews/CeloNewsFeedItem.tsx similarity index 98% rename from src/exchange/CeloNewsFeedItem.tsx rename to src/celoNews/CeloNewsFeedItem.tsx index 63178d0d178..c895d8badf9 100644 --- a/src/exchange/CeloNewsFeedItem.tsx +++ b/src/celoNews/CeloNewsFeedItem.tsx @@ -3,9 +3,9 @@ import * as React from 'react' import { Image, StyleSheet, Text, View } from 'react-native' import { CeloNewsEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' +import { CeloNewsArticle } from 'src/celoNews/types' import SkeletonPlaceholder from 'src/components/SkeletonPlaceholder' import Touchable from 'src/components/Touchable' -import { CeloNewsArticle } from 'src/exchange/types' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import colors from 'src/styles/colors' diff --git a/src/exchange/ExchangeHomeScreen.test.tsx b/src/celoNews/ExchangeHomeScreen.test.tsx similarity index 69% rename from src/exchange/ExchangeHomeScreen.test.tsx rename to src/celoNews/ExchangeHomeScreen.test.tsx index 404cb6505c2..95009d2524e 100644 --- a/src/exchange/ExchangeHomeScreen.test.tsx +++ b/src/celoNews/ExchangeHomeScreen.test.tsx @@ -1,16 +1,9 @@ import { render, within } from '@testing-library/react-native' import React from 'react' import { Provider } from 'react-redux' -import ExchangeHomeScreen from 'src/exchange/ExchangeHomeScreen' -import { getFeatureGate } from 'src/statsig' +import ExchangeHomeScreen from 'src/celoNews/ExchangeHomeScreen' import { createMockStore } from 'test/utils' -import { - exchangePriceHistory, - mockCeloTokenId, - mockCusdTokenId, - mockTokenBalances, - priceHistory, -} from 'test/values' +import { mockCeloTokenId, mockCusdTokenId, mockTokenBalances, priceHistory } from 'test/values' jest.mock('src/statsig') @@ -21,9 +14,6 @@ const store = createMockStore({ [mockCeloTokenId]: mockTokenBalances[mockCeloTokenId], }, }, - exchange: { - history: exchangePriceHistory, - }, priceHistory: { [mockCeloTokenId]: priceHistory, }, @@ -34,19 +24,7 @@ describe('ExchangeHomeScreen', () => { jest.resetAllMocks() }) - it('renders the price chart using firebase by default', async () => { - const { getByTestId } = render( - - - - ) - - // Check we can see the price chart - expect(getByTestId('PriceChart')).toBeTruthy() - }) - - it('renders the price chart using PriceHistoryChart and blockchain api when enabled', async () => { - jest.mocked(getFeatureGate).mockReturnValue(true) + it('renders the price chart using PriceHistoryChart and blockchain api', async () => { const { getByTestId } = render( diff --git a/src/exchange/ExchangeHomeScreen.tsx b/src/celoNews/ExchangeHomeScreen.tsx similarity index 86% rename from src/exchange/ExchangeHomeScreen.tsx rename to src/celoNews/ExchangeHomeScreen.tsx index a5db1e461c2..6cb7682c293 100644 --- a/src/exchange/ExchangeHomeScreen.tsx +++ b/src/celoNews/ExchangeHomeScreen.tsx @@ -6,10 +6,8 @@ import Animated from 'react-native-reanimated' import { SafeAreaView } from 'react-native-safe-area-context' import { CeloExchangeEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' +import CeloNewsFeed from 'src/celoNews/CeloNewsFeed' import Touchable from 'src/components/Touchable' -import CeloGoldHistoryChart from 'src/exchange/CeloGoldHistoryChart' -import CeloNewsFeed from 'src/exchange/CeloNewsFeed' -import { exchangeHistorySelector } from 'src/exchange/reducer' import InfoIcon from 'src/icons/InfoIcon' import { LocalCurrencyCode } from 'src/localCurrency/consts' import { convertDollarsToLocalAmount } from 'src/localCurrency/convert' @@ -20,8 +18,6 @@ import { Screens } from 'src/navigator/Screens' import PriceHistoryChart from 'src/priceHistory/PriceHistoryChart' import { useSelector } from 'src/redux/hooks' import DisconnectBanner from 'src/shared/DisconnectBanner' -import { getFeatureGate } from 'src/statsig' -import { StatsigFeatureGates } from 'src/statsig/types' import colors from 'src/styles/colors' import fontStyles from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' @@ -70,25 +66,16 @@ function ExchangeHomeScreen() { const tokensBySymbol = useSelector(tokensBySymbolSelector) const localCurrencyCode = useSelector(getLocalCurrencyCode) const localExchangeRate = useSelector(usdToLocalCurrencyRateSelector) - const exchangeHistory = useSelector(exchangeHistorySelector) - const usePriceHistoryFromBlockchainApi = getFeatureGate( - StatsigFeatureGates.USE_PRICE_HISTORY_FROM_BLOCKCHAIN_API - ) + const historicalPriceUsd = tokensBySymbol.CGLD?.historicalPricesUsd?.lastDay.price - const exchangeHistoryLength = exchangeHistory.aggregatedExchangeRates.length const lastKnownPriceUsd = - tokensBySymbol.CGLD?.lastKnownPriceUsd || - (exchangeHistoryLength && - exchangeHistory.aggregatedExchangeRates[exchangeHistoryLength - 1].exchangeRate) || - new BigNumber(0) + tokensBySymbol.CGLD?.lastKnownPriceUsd || historicalPriceUsd || new BigNumber(0) const currentGoldRateInLocalCurrency = dollarsToLocal(lastKnownPriceUsd) let rateChangeInPercentage, rateWentUp - if (exchangeHistoryLength) { - const oldestGoldRateInLocalCurrency = dollarsToLocal( - exchangeHistory.aggregatedExchangeRates[0].exchangeRate - ) + if (historicalPriceUsd) { + const oldestGoldRateInLocalCurrency = dollarsToLocal(historicalPriceUsd) if (oldestGoldRateInLocalCurrency) { const rateChange = currentGoldRateInLocalCurrency?.minus(oldestGoldRateInLocalCurrency) rateChangeInPercentage = currentGoldRateInLocalCurrency @@ -158,7 +145,7 @@ function ExchangeHomeScreen() { - {usePriceHistoryFromBlockchainApi && networkConfig.celoTokenId ? ( + {networkConfig.celoTokenId && ( - ) : ( - )} diff --git a/src/exchange/types.ts b/src/celoNews/types.ts similarity index 100% rename from src/exchange/types.ts rename to src/celoNews/types.ts diff --git a/src/exchange/CeloGoldHistoryChart.test.tsx b/src/exchange/CeloGoldHistoryChart.test.tsx deleted file mode 100644 index 03b06d4f5a4..00000000000 --- a/src/exchange/CeloGoldHistoryChart.test.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { render } from '@testing-library/react-native' -import * as React from 'react' -import 'react-native' -import { Provider } from 'react-redux' -import CeloGoldHistoryChart from 'src/exchange/CeloGoldHistoryChart' -import { createMockStore, getMockI18nProps } from 'test/utils' -import { exchangePriceHistory, mockCeloTokenId, mockTokenBalances } from 'test/values' - -const SAMPLE_BALANCE = '55.00001' - -it('renders without history', () => { - const tree = render( - - - - ) - expect(tree).toMatchSnapshot() -}) - -it('renders while update is in progress', () => { - const localExchangeRatePriceHistory = { ...exchangePriceHistory } - const endDate = new Date('01/01/2020').getTime() - localExchangeRatePriceHistory.lastTimeUpdated = 0 - localExchangeRatePriceHistory.celoGoldExchangeRates = [ - { - exchangeRate: '0.123', - timestamp: endDate, - }, - ] - localExchangeRatePriceHistory.aggregatedExchangeRates = [ - { - exchangeRate: '0.123', - timestamp: endDate, - }, - ] - - const tree = render( - - - - ) - expect(tree).toMatchSnapshot() -}) - -it('renders properly', () => { - const tree = render( - - - - ) - expect(tree).toMatchSnapshot() -}) diff --git a/src/exchange/CeloGoldHistoryChart.tsx b/src/exchange/CeloGoldHistoryChart.tsx deleted file mode 100644 index 20f5f00f254..00000000000 --- a/src/exchange/CeloGoldHistoryChart.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import BigNumber from 'bignumber.js' -import _ from 'lodash' -import React, { useCallback, useState } from 'react' -import { WithTranslation } from 'react-i18next' -import { - ActivityIndicator, - LayoutChangeEvent, - StyleSheet, - Text, - View, - ViewStyle, -} from 'react-native' -import { Circle, G, Line, Text as SvgText } from 'react-native-svg' -import { CeloExchangeEvents } from 'src/analytics/Events' -import ValoraAnalytics from 'src/analytics/ValoraAnalytics' -import { exchangeHistorySelector } from 'src/exchange/reducer' -import { withTranslation } from 'src/i18n' -import { LocalCurrencyCode } from 'src/localCurrency/consts' -import { convertDollarsToLocalAmount } from 'src/localCurrency/convert' -import { getLocalCurrencyCode, usdToLocalCurrencyRateSelector } from 'src/localCurrency/selectors' -import { useSelector } from 'src/redux/hooks' -import colors from 'src/styles/colors' -import { Spacing } from 'src/styles/styles' -import variables from 'src/styles/variables' -import { tokensBySymbolSelector } from 'src/tokens/selectors' -import { getLocalCurrencyDisplayValue } from 'src/utils/formatting' -import { formatFeedDate } from 'src/utils/time' -import { VictoryGroup, VictoryLine, VictoryScatter } from 'victory-native' - -const CHART_WIDTH = variables.width -const CHART_HEIGHT = 180 -const CHART_MIN_VERTICAL_RANGE = 0.1 // one cent -const CHART_DOMAIN_PADDING = { y: [30, 30] as [number, number], x: [5, 5] as [number, number] } - -interface OwnProps { - testID?: string - color?: colors - containerStyle?: ViewStyle - chartPadding?: number -} - -type Props = WithTranslation & OwnProps - -// ChartAwareSvgText draws text on the chart with avareness of its edges. -// Example: we want to draw some text at {x:10,y:10} (coordinates of the center). -// The text width is 33px and if draw it right away it will be cuted by the chart edges. -// The component will adjust coordinates to {x: (textWidthInPixels/2), y: 10} -function ChartAwareSvgText({ - position, - x, - y, - value, - chartWidth, -}: { - position: 'top' | 'bottom' - x: number - y: number - value: string - chartWidth: number -}) { - if (position === 'top') { - y = y - 16 - } else if (position === 'bottom') { - y = y + 25 - } - const [adjustedX, setAdjustedX] = useState(x) - const horizontalOffset = variables.contentPadding - const onLayout = useCallback( - ({ - nativeEvent: { - layout: { width }, - }, - }: LayoutChangeEvent) => { - if (Math.abs(width - chartWidth) > 2) { - if (x - width / 2 - horizontalOffset < 0) { - setAdjustedX(width / 2 + horizontalOffset) - } - if (x + width / 2 + horizontalOffset > chartWidth) { - setAdjustedX(chartWidth - width / 2 - horizontalOffset) - } - } - }, - [x] - ) - return ( - - {value} - - ) -} - -function renderPointOnChart( - chartData: Array<{ amount: number | BigNumber; displayValue: string }>, - chartWidth: number, - color: colors -) { - let lowestRateIdx = 0, - highestRateIdx = 0 - chartData.forEach((rate, idx) => { - if (rate.amount > chartData[highestRateIdx].amount) { - highestRateIdx = idx - } - if (rate.amount < chartData[lowestRateIdx].amount) { - lowestRateIdx = idx - } - }) - return ({ datum, x, y }: { x: number; y: number; datum: { _x: number; _y: number } }) => { - const idx = datum._x - const result = [] - switch (idx) { - case 0: - result.push( - - - - - ) - break - - case chartData.length - 1: - result.push( - - - - ) - break - } - switch (idx) { - case highestRateIdx: - result.push( - - ) - break - - case lowestRateIdx: - result.push( - - ) - break - } - - switch (result.length) { - case 0: - return null - case 1: - return result[0] - default: - return <>{result} - } - } -} - -function Loader({ color }: { color: colors }) { - return ( - - - - ) -} -/** - * @deprecated - Use priceHistory/PriceHistoryChart.tsx instead as it handles multiple tokens - */ -function CeloGoldHistoryChart({ - testID, - i18n, - color = colors.goldBrand, - containerStyle, - chartPadding = variables.contentPadding, -}: Props) { - const localCurrencyCode = useSelector(getLocalCurrencyCode) - const displayLocalCurrency = useCallback( - (amount: BigNumber.Value) => - getLocalCurrencyDisplayValue(amount, localCurrencyCode || LocalCurrencyCode.USD, true), - [localCurrencyCode] - ) - const localExchangeRate = useSelector(usdToLocalCurrencyRateSelector) - const dollarsToLocal = useCallback( - (amount: BigNumber.Value | null) => - convertDollarsToLocalAmount(amount, localCurrencyCode ? localExchangeRate : 1), - [localExchangeRate] - ) - const exchangeHistory = useSelector(exchangeHistorySelector) - const tokensBySymbol = useSelector(tokensBySymbolSelector) - - const onTap = useCallback(() => { - ValoraAnalytics.track(CeloExchangeEvents.celo_chart_tapped) - }, []) - - if (!exchangeHistory.aggregatedExchangeRates?.length) { - return - } - - const chartData = exchangeHistory.aggregatedExchangeRates.map((exchangeRate) => { - const localAmount = dollarsToLocal(exchangeRate.exchangeRate) - return { - amount: localAmount ? localAmount.toNumber() : 0, - displayValue: localAmount ? displayLocalCurrency(localAmount) : '', - } - }) - - const currentGoldRateInLocalCurrency = - tokensBySymbol.CGLD?.lastKnownPriceUsd?.toNumber() || - chartData[chartData.length - 1].amount || - null - const oldestGoldRateInLocalCurrency = chartData[0].amount - if (oldestGoldRateInLocalCurrency === null || currentGoldRateInLocalCurrency === null) { - return - } - // We need displayValue to show min/max on the chart. In case the - // current value is min/max we do not need to show it once again, - // therefor displayValue = '' - chartData.push({ - amount: currentGoldRateInLocalCurrency, - displayValue: displayLocalCurrency(currentGoldRateInLocalCurrency), - }) - const RenderPoint = renderPointOnChart(chartData, CHART_WIDTH, color) - - const values = chartData.map((el) => el.amount) - const min = Math.min(...values) - const max = Math.max(...values) - let domain - // ensure that vertical chart range is at least CHART_MIN_VERTICAL_RANGE - if (max - min < CHART_MIN_VERTICAL_RANGE) { - const offset = CHART_MIN_VERTICAL_RANGE - (max - min) / 2 - domain = { - y: [min - offset, max + offset] as [number, number], - x: [0, chartData.length - 1] as [number, number], - } - } - const latestExchangeRate = _.last(exchangeHistory.aggregatedExchangeRates)! - - return ( - - el.amount)} - domain={domain} - > - {/* - // @ts-ignore */} - } /> - - - - - {formatFeedDate(latestExchangeRate.timestamp - exchangeHistory.range, i18n)} - - {formatFeedDate(latestExchangeRate.timestamp, i18n)} - - - ) -} - -const styles = StyleSheet.create({ - container: { - marginBottom: Spacing.Thick24, - }, - loader: { - width: CHART_WIDTH, - height: CHART_HEIGHT + 37.5, - alignItems: 'center', - justifyContent: 'center', - }, - timeframe: { - color: colors.gray3, - fontSize: 16, - }, - range: { - marginTop: variables.contentPadding, - justifyContent: 'space-between', - flexDirection: 'row', - }, -}) - -export default withTranslation()(CeloGoldHistoryChart) diff --git a/src/exchange/__snapshots__/CeloGoldHistoryChart.test.tsx.snap b/src/exchange/__snapshots__/CeloGoldHistoryChart.test.tsx.snap deleted file mode 100644 index a33cc533dec..00000000000 --- a/src/exchange/__snapshots__/CeloGoldHistoryChart.test.tsx.snap +++ /dev/null @@ -1,406 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders properly 1`] = ` - - - - - - - - - - - ₱0.00 - - - ₱1.30 - - - - - - - - - - - - - - - - - - - - - - - Oct 4 - - - Nov 3 - - - -`; - -exports[`renders while update is in progress 1`] = ` - - - - - - - - - - - ₱0.16 - - - - - - - - - - - - - - - - - - - - - - - Dec 2 - - - Jan 1 - - - -`; - -exports[`renders without history 1`] = ` - - - -`; diff --git a/src/exchange/actions.ts b/src/exchange/actions.ts deleted file mode 100644 index f567caca1a1..00000000000 --- a/src/exchange/actions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ExchangeRate } from 'src/exchange/reducer' - -export enum Actions { - UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY = 'EXCHANGE/UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY', -} - -export interface UpdateCeloGoldExchangeRateHistory { - type: Actions.UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY - timestamp: number - exchangeRates: ExchangeRate[] -} - -export const updateCeloGoldExchangeRateHistory = ( - exchangeRates: ExchangeRate[], - timestamp: number -): UpdateCeloGoldExchangeRateHistory => ({ - type: Actions.UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY, - exchangeRates, - timestamp, -}) - -export type ActionTypes = UpdateCeloGoldExchangeRateHistory diff --git a/src/exchange/reducer.ts b/src/exchange/reducer.ts deleted file mode 100644 index 1bdd03b2624..00000000000 --- a/src/exchange/reducer.ts +++ /dev/null @@ -1,117 +0,0 @@ -import _ from 'lodash' -import { Actions, ActionTypes } from 'src/exchange/actions' -import { getRehydratePayload, REHYDRATE, RehydrateAction } from 'src/redux/persist-helper' -import { RootState } from 'src/redux/reducers' -import { Currency } from 'src/utils/currencies' - -export const MAX_HISTORY_RETENTION = 30 * 24 * 3600 * 1000 // (ms) ~ 30 days -export const ADDRESS_LENGTH = 42 - -export interface ExchangeRate { - exchangeRate: string - timestamp: number -} - -export type ExchangeRates = Record> - -interface State { - history: { - // TODO this should be remove once we have aggregation on - // blockchain api side - celoGoldExchangeRates: ExchangeRate[] - aggregatedExchangeRates: ExchangeRate[] - granularity: number - range: number - lastTimeUpdated: number - } -} - -export const initialState = { - history: { - celoGoldExchangeRates: [], - aggregatedExchangeRates: [], - granularity: 60, - range: 30 * 24 * 60 * 60 * 1000, // 30 days - lastTimeUpdated: 0, - }, -} - -export const exchangeHistorySelector = (state: RootState) => state.exchange.history - -function aggregateExchangeRates( - celoGoldExchangeRates: ExchangeRate[], - granularity: number, - range: number -): ExchangeRate[] { - if (!celoGoldExchangeRates.length) { - return [] - } - function calculateGroup(exchangeRate: ExchangeRate) { - return Math.floor(exchangeRate.timestamp / (range / granularity)) - } - const groupedExchangeHistory = _.groupBy(celoGoldExchangeRates, calculateGroup) - const latestExchangeRate = celoGoldExchangeRates[celoGoldExchangeRates.length - 1] - const latestGroup = calculateGroup(latestExchangeRate) - - return _.range(Math.min(granularity - 1, Object.keys(groupedExchangeHistory).length), 0, -1).map( - (i) => { - const group = groupedExchangeHistory[latestGroup - i + 1] - return { - exchangeRate: group ? _.meanBy(group, (er) => parseFloat(er.exchangeRate)).toString() : '0', - timestamp: _.first(group)?.timestamp ?? 0, - } - } - ) -} - -export const historyReducer = ( - state: State['history'] | undefined = initialState.history, - action: ActionTypes -): State['history'] => { - switch (action.type) { - case Actions.UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY: - const celoGoldExchangeRates = [ - ...state.celoGoldExchangeRates, - ...action.exchangeRates, - ].filter((er) => er.timestamp > action.timestamp - MAX_HISTORY_RETENTION) - return { - ...state, - celoGoldExchangeRates, - aggregatedExchangeRates: aggregateExchangeRates( - celoGoldExchangeRates, - state.granularity, - state.range - ), - lastTimeUpdated: action.timestamp, - } - default: - return state - } -} - -export const reducer = ( - state: State | undefined = initialState, - action: ActionTypes | RehydrateAction -): State => { - switch (action.type) { - case REHYDRATE: { - // Ignore some persisted properties - const persisted = getRehydratePayload(action, 'exchange') - return { - ...state, - ...persisted, - history: { - ...initialState.history, - ...persisted.history, - }, - } - } - case Actions.UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY: - return { - ...state, - history: historyReducer(state.history, action), - } - default: - return state - } -} diff --git a/src/firebase/saga.ts b/src/firebase/saga.ts index bd804f82078..1b8e68ab3de 100644 --- a/src/firebase/saga.ts +++ b/src/firebase/saga.ts @@ -1,14 +1,10 @@ import { sleep } from '@celo/utils/lib/async' import firebase from '@react-native-firebase/app' -import { FirebaseDatabaseTypes } from '@react-native-firebase/database' -import { eventChannel } from 'redux-saga' import { handleUpdateAccountRegistration } from 'src/account/saga' import { showError } from 'src/alert/actions' import { ErrorMessages } from 'src/app/ErrorMessages' import { Actions as AppActions } from 'src/app/actions' import { FIREBASE_ENABLED, isE2EEnv } from 'src/config' -import { updateCeloGoldExchangeRateHistory } from 'src/exchange/actions' -import { ExchangeRate, MAX_HISTORY_RETENTION, exchangeHistorySelector } from 'src/exchange/reducer' import { Actions, firebaseAuthorized } from 'src/firebase/actions' import { checkInitialNotification, @@ -21,11 +17,9 @@ import { setLanguage } from 'src/i18n/slice' import Logger from 'src/utils/Logger' import { safely } from 'src/utils/safely' import { getAccount } from 'src/web3/saga' -import { call, cancelled, put, select, spawn, take, takeEvery, takeLatest } from 'typed-redux-saga' +import { call, put, spawn, takeEvery, takeLatest } from 'typed-redux-saga' const TAG = 'firebase/saga' -const EXCHANGE_RATES = 'exchangeRates' -const VALUE_CHANGE_HOOK = 'value' const FIREBASE_CONNECT_RETRIES = 3 export function* waitForFirebaseAuth() { @@ -80,84 +74,9 @@ export function* watchLanguage() { yield* takeEvery(setLanguage.type, safely(syncLanguageSelection)) } -function celoGoldExchangeRateHistoryChannel(lastTimeUpdated: number) { - const errorCallback = (error: Error) => { - Logger.warn(TAG, error.toString()) - } - - const now = Date.now() - // timestamp + 1 is used because .startAt is inclusive - const startAt = Math.max(lastTimeUpdated + 1, now - MAX_HISTORY_RETENTION) - - return eventChannel((emit: any) => { - const singleItemEmitter = (snapshot: FirebaseDatabaseTypes.DataSnapshot) => { - emit([snapshot.val()]) - } - const listenForNewElements = (newElementsStartAt: number) => { - firebase - .database() - .ref(`${EXCHANGE_RATES}/cGLD/cUSD`) - .orderByChild('timestamp') - .startAt(newElementsStartAt) - .on('child_added', singleItemEmitter, errorCallback) - } - const fullListEmitter = (snapshot: FirebaseDatabaseTypes.DataSnapshot) => { - const result: ExchangeRate[] = [] - snapshot.forEach((childSnapshot: FirebaseDatabaseTypes.DataSnapshot) => { - result.push(childSnapshot.val()) - return undefined - }) - if (result.length) { - emit(result) - listenForNewElements(result[result.length - 1].timestamp + 1) - } else { - listenForNewElements(startAt) - } - } - - firebase - .database() - .ref(`${EXCHANGE_RATES}/cGLD/cUSD`) - .orderByChild('timestamp') - .startAt(startAt) - .once(VALUE_CHANGE_HOOK, fullListEmitter, errorCallback) - .catch((error) => { - Logger.error(TAG, 'Error while fetching exchange rates', error) - }) - - return () => { - firebase.database().ref(`${EXCHANGE_RATES}/cGLD/cUSD`).off() - } - }) -} - -export function* subscribeToCeloGoldExchangeRateHistory() { - yield* call(waitForFirebaseAuth) - const history = yield* select(exchangeHistorySelector) - const channel = yield* call(celoGoldExchangeRateHistoryChannel, history.lastTimeUpdated) - try { - while (true) { - const exchangeRates = (yield* take(channel)) as ExchangeRate[] - const now = Date.now() - yield* put(updateCeloGoldExchangeRateHistory(exchangeRates, now)) - } - } catch (error) { - Logger.error( - `${TAG}@subscribeToCeloGoldExchangeRateHistory`, - 'Failed to subscribe to celo gold exchange rate history', - error - ) - } finally { - if (yield* cancelled()) { - channel.close() - } - } -} - export function* firebaseSaga() { yield* spawn(initializeFirebase) yield* spawn(watchLanguage) - yield* spawn(subscribeToCeloGoldExchangeRateHistory) yield* takeLatest(AppActions.APP_MOUNTED, safely(watchFirebaseNotificationChannel)) yield* takeLatest(AppActions.APP_MOUNTED, safely(checkInitialNotification)) } diff --git a/src/navigator/DrawerNavigator.tsx b/src/navigator/DrawerNavigator.tsx index b48b15f6556..ec790d06a45 100644 --- a/src/navigator/DrawerNavigator.tsx +++ b/src/navigator/DrawerNavigator.tsx @@ -32,13 +32,13 @@ import { HomeEvents, RewardsEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' import { phoneNumberVerifiedSelector } from 'src/app/selectors' import BackupIntroduction from 'src/backup/BackupIntroduction' +import ExchangeHomeScreen from 'src/celoNews/ExchangeHomeScreen' import AccountNumber from 'src/components/AccountNumber' import ContactCircleSelf from 'src/components/ContactCircleSelf' import PhoneNumberWithFlag from 'src/components/PhoneNumberWithFlag' import { RewardsScreenOrigin } from 'src/consumerIncentives/analyticsEventsTracker' import { dappsListApiUrlSelector } from 'src/dapps/selectors' import DAppsExplorerScreenSearchFilter from 'src/dappsExplorer/DAppsExplorerScreenSearchFilter' -import ExchangeHomeScreen from 'src/exchange/ExchangeHomeScreen' import WalletHome from 'src/home/WalletHome' import AttentionIcon from 'src/icons/Attention' import { Home } from 'src/icons/Home' diff --git a/src/redux/migrations.test.ts b/src/redux/migrations.test.ts index 3fa39fb038b..ea86624b8ba 100644 --- a/src/redux/migrations.test.ts +++ b/src/redux/migrations.test.ts @@ -1,7 +1,6 @@ import BigNumber from 'bignumber.js' import _ from 'lodash' import { FinclusiveKycStatus } from 'src/account/reducer' -import { initialState as exchangeInitialState } from 'src/exchange/reducer' import { migrations } from 'src/redux/migrations' import { Network, @@ -83,11 +82,11 @@ import { mockInvitableRecipient, mockInvitableRecipient2, mockPositions, - mockShortcuts, + mockPositionsLegacy, mockRecipient, mockRecipient2, + mockShortcuts, mockShortcutsLegacy, - mockPositionsLegacy, } from 'test/values' describe('Redux persist migrations', () => { @@ -283,7 +282,13 @@ describe('Redux persist migrations', () => { const migratedSchema = migrations[12](stub) expect(migratedSchema.app).toEqual(appStub) expect(migratedSchema.exchange.otherExchangeProps).toEqual(exchangeStub) - expect(migratedSchema.exchange.history).toEqual(exchangeInitialState.history) + expect(migratedSchema.exchange.history).toEqual({ + celoGoldExchangeRates: [], + aggregatedExchangeRates: [], + granularity: 60, + range: 30 * 24 * 60 * 60 * 1000, + lastTimeUpdated: 0, + }) }) it('works for v12 to v13', () => { const stub = { diff --git a/src/redux/migrations.ts b/src/redux/migrations.ts index 9d529c5517e..ce69d56e22b 100644 --- a/src/redux/migrations.ts +++ b/src/redux/migrations.ts @@ -3,7 +3,6 @@ import { FinclusiveKycStatus, RecoveryPhraseInOnboardingStatus } from 'src/accou import { MultichainBetaStatus } from 'src/app/actions' import { DEFAULT_SENTRY_NETWORK_ERRORS, DEFAULT_SENTRY_TRACES_SAMPLE_RATE } from 'src/config' import { Dapp } from 'src/dapps/types' -import { initialState as exchangeInitialState } from 'src/exchange/reducer' import { CachedQuoteParams, SendingFiatAccountStatus } from 'src/fiatconnect/slice' import { REMOTE_CONFIG_VALUES_DEFAULTS } from 'src/firebase/remoteConfigValuesDefaults' import { AddressToDisplayNameType } from 'src/identity/reducer' @@ -41,6 +40,16 @@ export function updateCachedQuoteParams(cachedQuoteParams: { const DEFAULT_DAILY_PAYMENT_LIMIT_CUSD_LEGACY = 1000 +const exchangeInitialState = { + history: { + celoGoldExchangeRates: [], + aggregatedExchangeRates: [], + granularity: 60, + range: 30 * 24 * 60 * 60 * 1000, // 30 days + lastTimeUpdated: 0, + }, +} + export const migrations = { 0: (state: any) => { const e164NumberToAddressOld = state.identity.e164NumberToAddress @@ -1744,4 +1753,7 @@ export const migrations = { pointsConfigStatus: 'idle', }, }), + 208: (state: any) => ({ + ...(_.omit(state, 'exchange') as any), + }), } diff --git a/src/redux/reducers.ts b/src/redux/reducers.ts index 6da0d80bcc1..63469cd5270 100644 --- a/src/redux/reducers.ts +++ b/src/redux/reducers.ts @@ -7,7 +7,6 @@ import { appReducer as app } from 'src/app/reducers' import superchargeReducer from 'src/consumerIncentives/slice' import dappsReducer from 'src/dapps/slice' import { escrowReducer as escrow } from 'src/escrow/reducer' -import { reducer as exchange } from 'src/exchange/reducer' import { reducer as fees } from 'src/fees/reducer' import { reducer as fiatExchanges } from 'src/fiatExchanges/reducer' import fiatConnectReducer from 'src/fiatconnect/slice' @@ -20,6 +19,7 @@ import keylessBackupReducer from 'src/keylessBackup/slice' import { reducer as localCurrency } from 'src/localCurrency/reducer' import { reducer as networkInfo } from 'src/networkInfo/reducer' import nftsReducer from 'src/nfts/slice' +import pointsReducer from 'src/points/slice' import positionsReducer from 'src/positions/slice' import priceHistoryReducer from 'src/priceHistory/slice' import { recipientsReducer as recipients } from 'src/recipients/reducer' @@ -29,7 +29,6 @@ import tokenReducer from 'src/tokens/slice' import { reducer as transactions } from 'src/transactions/reducer' import { reducer as walletConnect } from 'src/walletConnect/reducer' import { reducer as web3 } from 'src/web3/reducer' -import pointsReducer from 'src/points/slice' const appReducer = combineReducers({ app, @@ -38,7 +37,6 @@ const appReducer = combineReducers({ alert, send, home, - exchange, transactions, web3, identity, diff --git a/src/redux/sagas.ts b/src/redux/sagas.ts index dde1cdf031a..e2271112306 100644 --- a/src/redux/sagas.ts +++ b/src/redux/sagas.ts @@ -18,10 +18,9 @@ import { superchargeSaga } from 'src/consumerIncentives/saga' import { dappKitSaga } from 'src/dappkit/dappkit' import { dappsSaga } from 'src/dapps/saga' import { escrowSaga } from 'src/escrow/saga' -import { Actions as ExchangeActions } from 'src/exchange/actions' import { feesSaga } from 'src/fees/saga' -import { fiatConnectSaga } from 'src/fiatconnect/saga' import { fiatExchangesSaga } from 'src/fiatExchanges/saga' +import { fiatConnectSaga } from 'src/fiatconnect/saga' import { firebaseSaga } from 'src/firebase/saga' import { homeSaga } from 'src/home/saga' import { i18nSaga } from 'src/i18n/saga' @@ -33,6 +32,7 @@ import { keylessBackupSaga } from 'src/keylessBackup/saga' import { localCurrencySaga } from 'src/localCurrency/saga' import { networkInfoSaga } from 'src/networkInfo/saga' import { nftsSaga } from 'src/nfts/saga' +import { pointsSaga } from 'src/points/saga' import { positionsSaga } from 'src/positions/saga' import { priceHistorySaga } from 'src/priceHistory/saga' import { setPhoneRecipientCache, updateValoraRecipientCache } from 'src/recipients/reducer' @@ -43,19 +43,17 @@ import { swapSaga } from 'src/swap/saga' import { tokensSaga } from 'src/tokens/saga' import { Actions as TransactionActions } from 'src/transactions/actions' import { transactionSaga } from 'src/transactions/saga' -import { checkAccountExistenceSaga } from 'src/utils/accountChecker' import Logger from 'src/utils/Logger' +import { checkAccountExistenceSaga } from 'src/utils/accountChecker' import { walletConnectSaga } from 'src/walletConnect/saga' import { Actions as Web3Actions } from 'src/web3/actions' import { web3Saga } from 'src/web3/saga' -import { pointsSaga } from 'src/points/saga' import { call, select, spawn, take, takeEvery } from 'typed-redux-saga' const loggerBlocklist = [ REHYDRATE, AppActions.PHONE_NUMBER_VERIFICATION_COMPLETED, AccountActions.SET_PHONE_NUMBER, - ExchangeActions.UPDATE_CELO_GOLD_EXCHANGE_RATE_HISTORY, // Not private, just noisy ImportActions.IMPORT_BACKUP_PHRASE, setPhoneRecipientCache.toString(), updateValoraRecipientCache.toString(), diff --git a/src/redux/store.test.ts b/src/redux/store.test.ts index b2e85add0d7..dc073601a41 100644 --- a/src/redux/store.test.ts +++ b/src/redux/store.test.ts @@ -98,7 +98,7 @@ describe('store state', () => { { "_persist": { "rehydrated": true, - "version": 207, + "version": 208, }, "account": { "acceptedTerms": false, @@ -191,15 +191,6 @@ describe('store state', () => { "isReclaiming": false, "sentEscrowedPayments": [], }, - "exchange": { - "history": { - "aggregatedExchangeRates": [], - "celoGoldExchangeRates": [], - "granularity": 60, - "lastTimeUpdated": 0, - "range": 2592000000, - }, - }, "fees": { "estimates": {}, }, diff --git a/src/redux/store.ts b/src/redux/store.ts index 32ead25cbba..019c591c80c 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -21,7 +21,7 @@ const persistConfig: PersistConfig = { key: 'root', // default is -1, increment as we make migrations // See https://github.com/valora-inc/wallet/tree/main/WALLET.md#redux-state-migration - version: 207, + version: 208, keyPrefix: `reduxStore-`, // the redux-persist default is `persist:` which doesn't work with some file systems. storage: FSStorage(), blacklist: ['networkInfo', 'alert', 'imports', 'keylessBackup', 'jumpstart'], diff --git a/src/statsig/constants.ts b/src/statsig/constants.ts index fa423dc841a..fb588ed4fa4 100644 --- a/src/statsig/constants.ts +++ b/src/statsig/constants.ts @@ -14,7 +14,6 @@ export const FeatureGates = { [StatsigFeatureGates.SHOW_MULTICHAIN_BETA_SCREEN]: false, [StatsigFeatureGates.SHOW_BETA_TAG]: false, [StatsigFeatureGates.SAVE_CONTACTS]: false, - [StatsigFeatureGates.USE_PRICE_HISTORY_FROM_BLOCKCHAIN_API]: false, [StatsigFeatureGates.SHOW_GET_STARTED]: false, [StatsigFeatureGates.CLEVERTAP_INBOX]: false, [StatsigFeatureGates.SHOW_SWAP_TOKEN_FILTERS]: false, diff --git a/src/statsig/types.ts b/src/statsig/types.ts index 094b9f9c0b4..1ebcf789a21 100644 --- a/src/statsig/types.ts +++ b/src/statsig/types.ts @@ -21,7 +21,6 @@ export enum StatsigFeatureGates { SHOW_MULTICHAIN_BETA_SCREEN = 'show_multichain_beta_screen', SHOW_BETA_TAG = 'show_beta_tag', SAVE_CONTACTS = 'save_contacts', - USE_PRICE_HISTORY_FROM_BLOCKCHAIN_API = 'use_price_history_from_blockchain_api', SHOW_GET_STARTED = 'show_get_started', CLEVERTAP_INBOX = 'clevertap_inbox', SHOW_SWAP_TOKEN_FILTERS = 'show_swap_token_filters', diff --git a/src/tokens/TokenDetails.test.tsx b/src/tokens/TokenDetails.test.tsx index ea7c128ea44..d93ffed4525 100644 --- a/src/tokens/TokenDetails.test.tsx +++ b/src/tokens/TokenDetails.test.tsx @@ -13,7 +13,6 @@ import { ONE_DAY_IN_MILLIS } from 'src/utils/time' import MockedNavigator from 'test/MockedNavigator' import { createMockStore } from 'test/utils' import { - exchangePriceHistory, mockCeloTokenId, mockPoofTokenId, mockTestTokenTokenId, @@ -178,26 +177,7 @@ describe('TokenDetails', () => { expect(queryByText('tokenDetails.priceUnavailable')).toBeFalsy() }) - it('renders chart and news feed if token is native (celo) using firebase', () => { - jest.mocked(getFeatureGate).mockReturnValue(false) // Use old prices from firebase - const store = createMockStore({ - exchange: { - history: exchangePriceHistory, - }, - }) - - const { getByTestId, queryByText } = render( - - - - ) - - expect(getByTestId('TokenDetails/Chart')).toBeTruthy() - expect(queryByText('celoNews.headerTitle')).toBeTruthy() - }) - it('renders chart loader using blockchain API', () => { - jest.mocked(getFeatureGate).mockReturnValue(true) // Use new prices from blockchain API const store = createMockStore({ priceHistory: { [mockCeloTokenId]: { diff --git a/src/tokens/TokenDetails.tsx b/src/tokens/TokenDetails.tsx index 8f6b072c7e9..c7812994b75 100644 --- a/src/tokens/TokenDetails.tsx +++ b/src/tokens/TokenDetails.tsx @@ -6,6 +6,7 @@ import { SafeAreaView } from 'react-native-safe-area-context' import { AssetsEvents } from 'src/analytics/Events' import { TokenProperties } from 'src/analytics/Properties' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' +import CeloNewsFeed from 'src/celoNews/CeloNewsFeed' import BackButton from 'src/components/BackButton' import { BottomSheetRefType } from 'src/components/BottomSheet' import Button, { BtnSizes } from 'src/components/Button' @@ -14,8 +15,6 @@ import TokenDisplay from 'src/components/TokenDisplay' import TokenIcon, { IconSize } from 'src/components/TokenIcon' import Touchable from 'src/components/Touchable' import CustomHeader from 'src/components/header/CustomHeader' -import CeloGoldHistoryChart from 'src/exchange/CeloGoldHistoryChart' -import CeloNewsFeed from 'src/exchange/CeloNewsFeed' import { CICOFlow } from 'src/fiatExchanges/utils' import ArrowRightThick from 'src/icons/ArrowRightThick' import DataDown from 'src/icons/DataDown' @@ -34,8 +33,6 @@ import { StackParamList } from 'src/navigator/types' import PriceHistoryChart from 'src/priceHistory/PriceHistoryChart' import { useSelector } from 'src/redux/hooks' import { NETWORK_NAMES } from 'src/shared/conts' -import { getFeatureGate } from 'src/statsig' -import { StatsigFeatureGates } from 'src/statsig/types' import Colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' @@ -70,9 +67,6 @@ export default function TokenDetailsScreen({ route }: Props) { const actions = useActions(token) const tokenDetailsMoreActionsBottomSheetRef = useRef(null) const localCurrencySymbol = useSelector(getLocalCurrencySymbol) - const usePriceHistoryFromBlockchainApi = getFeatureGate( - StatsigFeatureGates.USE_PRICE_HISTORY_FROM_BLOCKCHAIN_API - ) return ( @@ -97,7 +91,7 @@ export default function TokenDetailsScreen({ route }: Props) { errorFallback={(localCurrencySymbol ?? '$').concat(' --')} /> {!token.isStableCoin && } - {token.isNative && usePriceHistoryFromBlockchainApi ? ( + {token.isNative && ( - ) : ( - token.tokenId === networkConfig.celoTokenId && ( - - ) )} { switch (action.type) { case REHYDRATE: { diff --git a/test/RootStateSchema.json b/test/RootStateSchema.json index c4d9495f296..c2712d5c8df 100644 --- a/test/RootStateSchema.json +++ b/test/RootStateSchema.json @@ -1237,22 +1237,6 @@ ], "type": "object" }, - "ExchangeRate": { - "additionalProperties": false, - "properties": { - "exchangeRate": { - "type": "string" - }, - "timestamp": { - "type": "number" - } - }, - "required": [ - "exchangeRate", - "timestamp" - ], - "type": "object" - }, "Fee": { "additionalProperties": false, "properties": { @@ -3544,121 +3528,6 @@ "type": "object" }, "State_10": { - "additionalProperties": false, - "properties": { - "acceptedTerms": { - "type": "boolean" - }, - "accountCreationTime": { - "type": "number" - }, - "accountToRecoverFromStoreWipe": { - "type": "string" - }, - "backupCompleted": { - "type": "boolean" - }, - "celoEducationCompleted": { - "type": "boolean" - }, - "choseToRestoreAccount": { - "type": "boolean" - }, - "cloudBackupCompleted": { - "type": "boolean" - }, - "contactDetails": { - "$ref": "#/definitions/UserContactDetails" - }, - "defaultCountryCode": { - "type": [ - "null", - "string" - ] - }, - "devModeActive": { - "type": "boolean" - }, - "devModeClickCount": { - "type": "number" - }, - "dismissedGetVerified": { - "type": "boolean" - }, - "dismissedGoldEducation": { - "type": "boolean" - }, - "dismissedKeepSupercharging": { - "type": "boolean" - }, - "dismissedStartSupercharging": { - "type": "boolean" - }, - "e164PhoneNumber": { - "type": [ - "null", - "string" - ] - }, - "hasMigratedToNewBip39": { - "type": "boolean" - }, - "name": { - "type": [ - "null", - "string" - ] - }, - "photosNUXClicked": { - "type": "boolean" - }, - "pictureUri": { - "type": [ - "null", - "string" - ] - }, - "pincodeType": { - "$ref": "#/definitions/PincodeType" - }, - "profileUploaded": { - "type": "boolean" - }, - "recoveringFromStoreWipe": { - "type": "boolean" - }, - "recoveryPhraseInOnboardingStatus": { - "$ref": "#/definitions/RecoveryPhraseInOnboardingStatus" - }, - "startOnboardingTime": { - "type": "number" - } - }, - "required": [ - "acceptedTerms", - "accountCreationTime", - "backupCompleted", - "celoEducationCompleted", - "cloudBackupCompleted", - "contactDetails", - "defaultCountryCode", - "devModeActive", - "devModeClickCount", - "dismissedGetVerified", - "dismissedGoldEducation", - "dismissedKeepSupercharging", - "dismissedStartSupercharging", - "e164PhoneNumber", - "hasMigratedToNewBip39", - "name", - "photosNUXClicked", - "pictureUri", - "pincodeType", - "recoveryPhraseInOnboardingStatus" - ], - "type": "object" - }, - "State_11": { "additionalProperties": false, "properties": { "isReclaiming": { @@ -3677,7 +3546,7 @@ ], "type": "object" }, - "State_12": { + "State_11": { "additionalProperties": false, "properties": { "estimates": { @@ -3689,7 +3558,7 @@ ], "type": "object" }, - "State_13": { + "State_12": { "additionalProperties": false, "properties": { "coinbasePaySenders": { @@ -3726,7 +3595,7 @@ ], "type": "object" }, - "State_14": { + "State_13": { "additionalProperties": false, "properties": { "error": { @@ -3849,7 +3718,7 @@ ], "type": "object" }, - "State_15": { + "State_14": { "additionalProperties": false, "properties": { "isImportingWallet": { @@ -3861,7 +3730,7 @@ ], "type": "object" }, - "State_16": { + "State_15": { "additionalProperties": false, "properties": { "providerLogos": { @@ -3877,7 +3746,7 @@ ], "type": "object" }, - "State_17": { + "State_16": { "additionalProperties": false, "properties": { "pendingActions": { @@ -3906,7 +3775,7 @@ ], "type": "object" }, - "State_18": { + "State_17": { "additionalProperties": false, "properties": { "error": { @@ -3926,7 +3795,7 @@ ], "type": "object" }, - "State_19": { + "State_18": { "additionalProperties": false, "properties": { "availableRewards": { @@ -3961,27 +3830,7 @@ ], "type": "object" }, - "State_2": { - "additionalProperties": false, - "properties": { - "connected": { - "type": "boolean" - }, - "rehydrated": { - "type": "boolean" - }, - "userLocationData": { - "$ref": "#/definitions/UserLocationData" - } - }, - "required": [ - "connected", - "rehydrated", - "userLocationData" - ], - "type": "object" - }, - "State_20": { + "State_19": { "additionalProperties": false, "properties": { "activeDapp": { @@ -4061,7 +3910,27 @@ ], "type": "object" }, - "State_21": { + "State_2": { + "additionalProperties": false, + "properties": { + "connected": { + "type": "boolean" + }, + "rehydrated": { + "type": "boolean" + }, + "userLocationData": { + "$ref": "#/definitions/UserLocationData" + } + }, + "required": [ + "connected", + "rehydrated", + "userLocationData" + ], + "type": "object" + }, + "State_20": { "additionalProperties": false, "properties": { "attemptReturnUserFlowLoading": { @@ -4216,7 +4085,7 @@ ], "type": "object" }, - "State_22": { + "State_21": { "additionalProperties": false, "properties": { "currentSwap": { @@ -4247,7 +4116,7 @@ ], "type": "object" }, - "State_23": { + "State_22": { "additionalProperties": false, "properties": { "positions": { @@ -4288,7 +4157,7 @@ ], "type": "object" }, - "State_24": { + "State_23": { "additionalProperties": false, "properties": { "backupStatus": { @@ -4329,7 +4198,7 @@ ], "type": "object" }, - "State_26": { + "State_25": { "additionalProperties": { "additionalProperties": false, "properties": { @@ -4351,7 +4220,7 @@ }, "type": "object" }, - "State_27": { + "State_26": { "additionalProperties": false, "properties": { "claimStatus": { @@ -4388,7 +4257,7 @@ ], "type": "object" }, - "State_28": { + "State_27": { "additionalProperties": false, "properties": { "getHistoryStatus": { @@ -4564,49 +4433,6 @@ "type": "object" }, "State_6": { - "additionalProperties": false, - "properties": { - "history": { - "additionalProperties": false, - "properties": { - "aggregatedExchangeRates": { - "items": { - "$ref": "#/definitions/ExchangeRate" - }, - "type": "array" - }, - "celoGoldExchangeRates": { - "items": { - "$ref": "#/definitions/ExchangeRate" - }, - "type": "array" - }, - "granularity": { - "type": "number" - }, - "lastTimeUpdated": { - "type": "number" - }, - "range": { - "type": "number" - } - }, - "required": [ - "aggregatedExchangeRates", - "celoGoldExchangeRates", - "granularity", - "lastTimeUpdated", - "range" - ], - "type": "object" - } - }, - "required": [ - "history" - ], - "type": "object" - }, - "State_7": { "additionalProperties": false, "properties": { "inviteTransactions": { @@ -4637,7 +4463,7 @@ ], "type": "object" }, - "State_8": { + "State_7": { "additionalProperties": false, "properties": { "account": { @@ -4677,7 +4503,7 @@ ], "type": "object" }, - "State_9": { + "State_8": { "additionalProperties": false, "properties": { "addressToDataEncryptionKey": { @@ -4736,6 +4562,121 @@ ], "type": "object" }, + "State_9": { + "additionalProperties": false, + "properties": { + "acceptedTerms": { + "type": "boolean" + }, + "accountCreationTime": { + "type": "number" + }, + "accountToRecoverFromStoreWipe": { + "type": "string" + }, + "backupCompleted": { + "type": "boolean" + }, + "celoEducationCompleted": { + "type": "boolean" + }, + "choseToRestoreAccount": { + "type": "boolean" + }, + "cloudBackupCompleted": { + "type": "boolean" + }, + "contactDetails": { + "$ref": "#/definitions/UserContactDetails" + }, + "defaultCountryCode": { + "type": [ + "null", + "string" + ] + }, + "devModeActive": { + "type": "boolean" + }, + "devModeClickCount": { + "type": "number" + }, + "dismissedGetVerified": { + "type": "boolean" + }, + "dismissedGoldEducation": { + "type": "boolean" + }, + "dismissedKeepSupercharging": { + "type": "boolean" + }, + "dismissedStartSupercharging": { + "type": "boolean" + }, + "e164PhoneNumber": { + "type": [ + "null", + "string" + ] + }, + "hasMigratedToNewBip39": { + "type": "boolean" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "photosNUXClicked": { + "type": "boolean" + }, + "pictureUri": { + "type": [ + "null", + "string" + ] + }, + "pincodeType": { + "$ref": "#/definitions/PincodeType" + }, + "profileUploaded": { + "type": "boolean" + }, + "recoveringFromStoreWipe": { + "type": "boolean" + }, + "recoveryPhraseInOnboardingStatus": { + "$ref": "#/definitions/RecoveryPhraseInOnboardingStatus" + }, + "startOnboardingTime": { + "type": "number" + } + }, + "required": [ + "acceptedTerms", + "accountCreationTime", + "backupCompleted", + "celoEducationCompleted", + "cloudBackupCompleted", + "contactDetails", + "defaultCountryCode", + "devModeActive", + "devModeClickCount", + "dismissedGetVerified", + "dismissedGoldEducation", + "dismissedKeepSupercharging", + "dismissedStartSupercharging", + "e164PhoneNumber", + "hasMigratedToNewBip39", + "name", + "photosNUXClicked", + "pictureUri", + "pincodeType", + "recoveryPhraseInOnboardingStatus" + ], + "type": "object" + }, "Status": { "enum": [ "error", @@ -6248,7 +6189,7 @@ "$ref": "#/definitions/PersistState" }, "account": { - "$ref": "#/definitions/State_10" + "$ref": "#/definitions/State_9" }, "alert": { "$ref": "#/definitions/State_3" @@ -6257,22 +6198,19 @@ "$ref": "#/definitions/State" }, "dapps": { - "$ref": "#/definitions/State_20" + "$ref": "#/definitions/State_19" }, "escrow": { - "$ref": "#/definitions/State_11" - }, - "exchange": { - "$ref": "#/definitions/State_6" + "$ref": "#/definitions/State_10" }, "fees": { - "$ref": "#/definitions/State_12" + "$ref": "#/definitions/State_11" }, "fiatConnect": { - "$ref": "#/definitions/State_21" + "$ref": "#/definitions/State_20" }, "fiatExchanges": { - "$ref": "#/definitions/State_16" + "$ref": "#/definitions/State_15" }, "home": { "$ref": "#/definitions/State_5" @@ -6281,19 +6219,19 @@ "$ref": "#/definitions/State_1" }, "identity": { - "$ref": "#/definitions/State_9" + "$ref": "#/definitions/State_8" }, "imports": { - "$ref": "#/definitions/State_15" + "$ref": "#/definitions/State_14" }, "jumpstart": { - "$ref": "#/definitions/State_27" + "$ref": "#/definitions/State_26" }, "keylessBackup": { - "$ref": "#/definitions/State_24" + "$ref": "#/definitions/State_23" }, "localCurrency": { - "$ref": "#/definitions/State_14" + "$ref": "#/definitions/State_13" }, "networkInfo": { "$ref": "#/definitions/State_2" @@ -6325,37 +6263,37 @@ "type": "object" }, "points": { - "$ref": "#/definitions/State_28" + "$ref": "#/definitions/State_27" }, "positions": { - "$ref": "#/definitions/State_23" + "$ref": "#/definitions/State_22" }, "priceHistory": { - "$ref": "#/definitions/State_26" + "$ref": "#/definitions/State_25" }, "recipients": { - "$ref": "#/definitions/State_13" + "$ref": "#/definitions/State_12" }, "send": { "$ref": "#/definitions/State_4" }, "supercharge": { - "$ref": "#/definitions/State_19" + "$ref": "#/definitions/State_18" }, "swap": { - "$ref": "#/definitions/State_22" + "$ref": "#/definitions/State_21" }, "tokens": { - "$ref": "#/definitions/State_18" + "$ref": "#/definitions/State_17" }, "transactions": { - "$ref": "#/definitions/State_7" + "$ref": "#/definitions/State_6" }, "walletConnect": { - "$ref": "#/definitions/State_17" + "$ref": "#/definitions/State_16" }, "web3": { - "$ref": "#/definitions/State_8" + "$ref": "#/definitions/State_7" } }, "required": [ @@ -6365,7 +6303,6 @@ "app", "dapps", "escrow", - "exchange", "fees", "fiatConnect", "fiatExchanges", diff --git a/test/schemas.ts b/test/schemas.ts index fc179e7db7f..52cbcd2a1da 100644 --- a/test/schemas.ts +++ b/test/schemas.ts @@ -3249,6 +3249,14 @@ export const v207Schema = { }, } +export const v208Schema = { + ..._.omit(v207Schema, ['exchange']), + _persist: { + ...v206Schema._persist, + version: 208, + }, +} + export function getLatestSchema(): Partial { - return v207Schema as Partial + return v208Schema as Partial } From 60c8612be453bbe148cfccd32fd53fc1b283ef0a Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:43:28 -0600 Subject: [PATCH 02/14] add todo --- src/account/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account/utils.ts b/src/account/utils.ts index ca6ce4ec101..275772dab55 100644 --- a/src/account/utils.ts +++ b/src/account/utils.ts @@ -1,6 +1,6 @@ import { parsePhoneNumber } from '@celo/phone-utils' -const ADDRESS_LENGTH = 42 +const ADDRESS_LENGTH = 42 // TODO(ACT-1173): see if this can be replaced with a viem helper export const isAddressFormat = (content: string): boolean => { return content.startsWith('0x') && content.length === ADDRESS_LENGTH From d764c94598e4ba3ddf2328c8a30be8e4246da4ef Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:50:36 -0600 Subject: [PATCH 03/14] fix merge --- test/schemas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/schemas.ts b/test/schemas.ts index 5f3d1f226dc..1a127bbdd1d 100644 --- a/test/schemas.ts +++ b/test/schemas.ts @@ -3250,7 +3250,6 @@ export const v207Schema = { } export const v208Schema = { - ...v207Schema, _persist: { ...v207Schema._persist, From 4cd52daa4d21f913faa8b90d000950afb83ec180 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:42:23 -0600 Subject: [PATCH 04/14] fix merge --- src/redux/migrations.ts | 2 +- src/redux/store.test.ts | 2 +- src/redux/store.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/redux/migrations.ts b/src/redux/migrations.ts index 1c5bc0126e4..15adfa1dc28 100644 --- a/src/redux/migrations.ts +++ b/src/redux/migrations.ts @@ -1774,7 +1774,7 @@ export const migrations = { pointsHistory: [], }, }), - 211 (state: any) => ({ + 211: (state: any) => ({ ...(_.omit(state, 'exchange') as any), }), } diff --git a/src/redux/store.test.ts b/src/redux/store.test.ts index 3a84810a805..bdaed07fd14 100644 --- a/src/redux/store.test.ts +++ b/src/redux/store.test.ts @@ -98,7 +98,7 @@ describe('store state', () => { { "_persist": { "rehydrated": true, - "version": 210, + "version": 211, }, "account": { "acceptedTerms": false, diff --git a/src/redux/store.ts b/src/redux/store.ts index 48ae87d249f..f26782ecc57 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -21,7 +21,7 @@ const persistConfig: PersistConfig = { key: 'root', // default is -1, increment as we make migrations // See https://github.com/valora-inc/wallet/tree/main/WALLET.md#redux-state-migration - version: 210, + version: 211, keyPrefix: `reduxStore-`, // the redux-persist default is `persist:` which doesn't work with some file systems. storage: FSStorage(), blacklist: ['networkInfo', 'alert', 'imports', 'keylessBackup', 'jumpstart'], From 5601ca3f4a6a29507bd38ee02ad417b1a5c6ed9f Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:42:46 -0600 Subject: [PATCH 05/14] fix merge --- test/schemas.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/schemas.ts b/test/schemas.ts index 7d26b4fbab0..496bcf1b934 100644 --- a/test/schemas.ts +++ b/test/schemas.ts @@ -3285,14 +3285,14 @@ export const v210Schema = { }, } -export const v210Schema = { +export const v211Schema = { ..._.omit(v209Schema, ['exchange']), _persist: { ...v206Schema._persist, - version: 210, + version: 211, }, } export function getLatestSchema(): Partial { - return v210Schema as Partial + return v211Schema as Partial } From f030fe8f3aa50a7ec2ae92bdd7275c8578c40192 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:10:10 -0600 Subject: [PATCH 06/14] fix schema --- test/schemas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/schemas.ts b/test/schemas.ts index a299020900b..1dbef8ef245 100644 --- a/test/schemas.ts +++ b/test/schemas.ts @@ -3287,9 +3287,9 @@ export const v210Schema = { } export const v211Schema = { - ..._.omit(v209Schema, ['exchange']), + ..._.omit(v210Schema, ['exchange']), _persist: { - ...v206Schema._persist, + ...v210Schema._persist, version: 211, }, } From b35ae21e4bc513a13648f921f3ef01c565f0db03 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:46:43 -0600 Subject: [PATCH 07/14] fix e2e test --- e2e/src/usecases/Assets.js | 1 + src/tokens/TokenDetails.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index 23510075f05..c8254f311a7 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -154,6 +154,7 @@ export default Assets = () => { if (learnMore) { it('learn more navigates to coingecko page', async () => { + await scrollIntoView('Learn more about', 'SettingsScrollView') await waitForElementByIdAndTap('TokenDetails/LearnMore') await waitForElementId('RNWebView') await waitFor(element(by.text('www.coingecko.com'))) diff --git a/src/tokens/TokenDetails.tsx b/src/tokens/TokenDetails.tsx index c7812994b75..574a0315f3e 100644 --- a/src/tokens/TokenDetails.tsx +++ b/src/tokens/TokenDetails.tsx @@ -71,7 +71,7 @@ export default function TokenDetailsScreen({ route }: Props) { return ( } /> - + Date: Thu, 25 Apr 2024 16:13:36 -0600 Subject: [PATCH 08/14] update import --- e2e/src/usecases/Assets.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index c8254f311a7..072d0ce2d84 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -1,7 +1,12 @@ import { generateMnemonic } from '@celo/cryptographic-utils' import { DEFAULT_RECIPIENT_ADDRESS, SAMPLE_BACKUP_KEY } from '../utils/consts' import { launchApp } from '../utils/retries' -import { quickOnboarding, waitForElementByIdAndTap, waitForElementId } from '../utils/utils' +import { + quickOnboarding, + waitForElementByIdAndTap, + waitForElementId, + scrollIntoView, +} from '../utils/utils' async function validateSendFlow(tokenSymbol) { // navigate to send amount screen to ensure the expected token symbol is pre-selected From 7bc2225b4aae59189762e7e832438ca6dfa9c339 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:43:50 -0600 Subject: [PATCH 09/14] fix scroll name --- e2e/src/usecases/Assets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index 072d0ce2d84..02cbbfd4e63 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -159,7 +159,7 @@ export default Assets = () => { if (learnMore) { it('learn more navigates to coingecko page', async () => { - await scrollIntoView('Learn more about', 'SettingsScrollView') + await scrollIntoView('Learn more about', 'TokenDetailsScrollView') await waitForElementByIdAndTap('TokenDetails/LearnMore') await waitForElementId('RNWebView') await waitFor(element(by.text('www.coingecko.com'))) From c65ad1763f75c6d3848667dd87ae670af183bd57 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Fri, 26 Apr 2024 09:30:15 -0600 Subject: [PATCH 10/14] fix scrollTo string --- e2e/src/usecases/Assets.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index 02cbbfd4e63..d732ccc405f 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -159,7 +159,10 @@ export default Assets = () => { if (learnMore) { it('learn more navigates to coingecko page', async () => { - await scrollIntoView('Learn more about', 'TokenDetailsScrollView') + await scrollIntoView( + `Learn more about ${symbol === 'CELO' ? 'Celo' : symbol}`, + 'TokenDetailsScrollView' + ) await waitForElementByIdAndTap('TokenDetails/LearnMore') await waitForElementId('RNWebView') await waitFor(element(by.text('www.coingecko.com'))) From 43659876fc1ddab62084aea9acb990862713465e Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:28:29 -0600 Subject: [PATCH 11/14] fix test --- e2e/src/usecases/Assets.js | 134 +++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index d732ccc405f..70758e56bf1 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -39,6 +39,7 @@ export default Assets = () => { { tokenId: 'celo-alfajores:native', symbol: 'CELO', + name: 'Celo', actions: ['Send', 'Add'], moreActions: ['Send', 'Add', 'Withdraw'], learnMore: true, @@ -46,6 +47,7 @@ export default Assets = () => { { tokenId: 'celo-alfajores:0x048f47d358ec521a6cf384461d674750a3cb58c8', symbol: 'TT', + name: 'Test Token', actions: ['Send'], moreActions: [], learnMore: false, @@ -58,6 +60,7 @@ export default Assets = () => { { tokenId: 'celo-alfajores:native', symbol: 'CELO', + name: 'Celo', actions: ['Add'], moreActions: [], learnMore: true, @@ -65,6 +68,7 @@ export default Assets = () => { { tokenId: 'celo-alfajores:0x874069fa1eb16d44d622f2e0ca25eea172369bc1', symbol: 'cUSD', + name: 'Celo Dollar', actions: ['Add'], moreActions: [], learnMore: true, @@ -104,79 +108,79 @@ export default Assets = () => { await expect(element(by.id('TokenBalanceItem')).atIndex(0)).toBeVisible() }) - describe.each(tokens)('For $symbol', ({ symbol, tokenId, learnMore, actions, moreActions }) => { - it('navigates to asset details on tapping asset', async () => { - await waitForElementByIdAndTap(`TokenBalanceItemTouchable/${tokenId}`) - await waitForElementId('TokenDetails/AssetValue') - }) - - if (actions.includes('Send')) { - it('send action navigates to send flow', async () => { - await element(by.id('TokenDetails/Action/Send')).tap() - await validateSendFlow(symbol) + describe.each(tokens)( + 'For $symbol', + ({ symbol, tokenId, name, learnMore, actions, moreActions }) => { + it('navigates to asset details on tapping asset', async () => { + await waitForElementByIdAndTap(`TokenBalanceItemTouchable/${tokenId}`) await waitForElementId('TokenDetails/AssetValue') }) - } - if (actions.includes('Add')) { - it('add action navigates to add cico flow', async () => { - await element(by.id('TokenDetails/Action/Add')).tap() - await validateAddFlow(symbol) - await waitForElementId('TokenDetails/AssetValue') - }) - } + if (actions.includes('Send')) { + it('send action navigates to send flow', async () => { + await element(by.id('TokenDetails/Action/Send')).tap() + await validateSendFlow(symbol) + await waitForElementId('TokenDetails/AssetValue') + }) + } - if (moreActions.includes('Send')) { - it('send action under more actions navigates to send flow', async () => { - await element(by.id('TokenDetails/Action/More')).tap() - await waitForElementByIdAndTap('TokenDetailsMoreActions/Send') - await validateSendFlow(symbol) - await element(by.id('TokenDetailsMoreActions')).swipe('down') - await waitForElementId('TokenDetails/AssetValue') - }) - } + if (actions.includes('Add')) { + it('add action navigates to add cico flow', async () => { + await element(by.id('TokenDetails/Action/Add')).tap() + await validateAddFlow(symbol) + await waitForElementId('TokenDetails/AssetValue') + }) + } - if (moreActions.includes('Add')) { - it('add action under more actions navigates to add cico flow', async () => { - await element(by.id('TokenDetails/Action/More')).tap() - await waitForElementByIdAndTap('TokenDetailsMoreActions/Add') - await validateAddFlow(symbol) - await element(by.id('TokenDetailsMoreActions')).swipe('down') - await waitForElementId('TokenDetails/AssetValue') - }) - } + if (moreActions.includes('Send')) { + it('send action under more actions navigates to send flow', async () => { + await element(by.id('TokenDetails/Action/More')).tap() + await waitForElementByIdAndTap('TokenDetailsMoreActions/Send') + await validateSendFlow(symbol) + await element(by.id('TokenDetailsMoreActions')).swipe('down') + await waitForElementId('TokenDetails/AssetValue') + }) + } - if (moreActions.includes('Withdraw')) { - it('withdraw action under more actions navigates to withdraw spend screen', async () => { - await element(by.id('TokenDetails/Action/More')).tap() - await waitForElementByIdAndTap('TokenDetailsMoreActions/Withdraw') - await waitForElementId('FiatExchangeTokenBalance') - await element(by.id('BackChevron')).tap() - await element(by.id('TokenDetailsMoreActions')).swipe('down') - await waitForElementId('TokenDetails/AssetValue') - }) - } + if (moreActions.includes('Add')) { + it('add action under more actions navigates to add cico flow', async () => { + await element(by.id('TokenDetails/Action/More')).tap() + await waitForElementByIdAndTap('TokenDetailsMoreActions/Add') + await validateAddFlow(symbol) + await element(by.id('TokenDetailsMoreActions')).swipe('down') + await waitForElementId('TokenDetails/AssetValue') + }) + } - if (learnMore) { - it('learn more navigates to coingecko page', async () => { - await scrollIntoView( - `Learn more about ${symbol === 'CELO' ? 'Celo' : symbol}`, - 'TokenDetailsScrollView' - ) - await waitForElementByIdAndTap('TokenDetails/LearnMore') - await waitForElementId('RNWebView') - await waitFor(element(by.text('www.coingecko.com'))) - .toBeVisible() - .withTimeout(10 * 1000) - await element(by.id('WebViewScreen/CloseButton')).tap() - await waitForElementId('TokenDetails/AssetValue') + if (moreActions.includes('Withdraw')) { + it('withdraw action under more actions navigates to withdraw spend screen', async () => { + await element(by.id('TokenDetails/Action/More')).tap() + await waitForElementByIdAndTap('TokenDetailsMoreActions/Withdraw') + await waitForElementId('FiatExchangeTokenBalance') + await element(by.id('BackChevron')).tap() + await element(by.id('TokenDetailsMoreActions')).swipe('down') + await waitForElementId('TokenDetails/AssetValue') + }) + } + + if (learnMore) { + it('learn more navigates to coingecko page', async () => { + await scrollIntoView(`Learn more about ${name}`, 'TokenDetailsScrollView') + await waitForElementByIdAndTap('TokenDetails/LearnMore') + await waitForElementId('RNWebView') + await waitFor(element(by.text('www.coingecko.com'))) + .toBeVisible() + .withTimeout(10 * 1000) + await element(by.id('WebViewScreen/CloseButton')).tap() + await waitForElementId('TokenBalanceItem') + }) + } + + it('navigates back to Assets page', async () => { + await element(by.id('BackChevron')).tap() + await waitForElementId('Assets/TabBar') }) } - - it('navigates back to Assets page', async () => { - await element(by.id('BackChevron')).tap() - await waitForElementId('Assets/TabBar') - }) - }) + ) }) } From 63d0adf2cc41795f2640247e1f64d7c945778549 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:28:49 -0600 Subject: [PATCH 12/14] feedback --- src/redux/migrations.test.ts | 10 ++-------- src/redux/migrations.ts | 2 +- test/schemas.ts | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/redux/migrations.test.ts b/src/redux/migrations.test.ts index ea86624b8ba..acc10e4dea0 100644 --- a/src/redux/migrations.test.ts +++ b/src/redux/migrations.test.ts @@ -1,7 +1,7 @@ import BigNumber from 'bignumber.js' import _ from 'lodash' import { FinclusiveKycStatus } from 'src/account/reducer' -import { migrations } from 'src/redux/migrations' +import { exchangeInitialState, migrations } from 'src/redux/migrations' import { Network, NetworkId, @@ -282,13 +282,7 @@ describe('Redux persist migrations', () => { const migratedSchema = migrations[12](stub) expect(migratedSchema.app).toEqual(appStub) expect(migratedSchema.exchange.otherExchangeProps).toEqual(exchangeStub) - expect(migratedSchema.exchange.history).toEqual({ - celoGoldExchangeRates: [], - aggregatedExchangeRates: [], - granularity: 60, - range: 30 * 24 * 60 * 60 * 1000, - lastTimeUpdated: 0, - }) + expect(migratedSchema.exchange.history).toEqual(exchangeInitialState.history) }) it('works for v12 to v13', () => { const stub = { diff --git a/src/redux/migrations.ts b/src/redux/migrations.ts index 15adfa1dc28..332e8eb1492 100644 --- a/src/redux/migrations.ts +++ b/src/redux/migrations.ts @@ -40,7 +40,7 @@ export function updateCachedQuoteParams(cachedQuoteParams: { const DEFAULT_DAILY_PAYMENT_LIMIT_CUSD_LEGACY = 1000 -const exchangeInitialState = { +export const exchangeInitialState = { history: { celoGoldExchangeRates: [], aggregatedExchangeRates: [], diff --git a/test/schemas.ts b/test/schemas.ts index 1dbef8ef245..13ccb591002 100644 --- a/test/schemas.ts +++ b/test/schemas.ts @@ -3287,7 +3287,7 @@ export const v210Schema = { } export const v211Schema = { - ..._.omit(v210Schema, ['exchange']), + ..._.omit(v210Schema, 'exchange'), _persist: { ...v210Schema._persist, version: 211, From 70f73001aa4d5f762dfe09093926b629b3f92338 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:12:14 -0600 Subject: [PATCH 13/14] feedback --- e2e/src/usecases/Assets.js | 131 +++++++++++++++++------------------ e2e/src/utils/utils.js | 16 +++++ src/analytics/Events.tsx | 4 -- src/analytics/Properties.tsx | 2 - src/analytics/docs.ts | 4 +- 5 files changed, 80 insertions(+), 77 deletions(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index 70758e56bf1..aeb1b230fab 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -39,7 +39,6 @@ export default Assets = () => { { tokenId: 'celo-alfajores:native', symbol: 'CELO', - name: 'Celo', actions: ['Send', 'Add'], moreActions: ['Send', 'Add', 'Withdraw'], learnMore: true, @@ -47,7 +46,6 @@ export default Assets = () => { { tokenId: 'celo-alfajores:0x048f47d358ec521a6cf384461d674750a3cb58c8', symbol: 'TT', - name: 'Test Token', actions: ['Send'], moreActions: [], learnMore: false, @@ -60,7 +58,6 @@ export default Assets = () => { { tokenId: 'celo-alfajores:native', symbol: 'CELO', - name: 'Celo', actions: ['Add'], moreActions: [], learnMore: true, @@ -68,7 +65,6 @@ export default Assets = () => { { tokenId: 'celo-alfajores:0x874069fa1eb16d44d622f2e0ca25eea172369bc1', symbol: 'cUSD', - name: 'Celo Dollar', actions: ['Add'], moreActions: [], learnMore: true, @@ -108,79 +104,76 @@ export default Assets = () => { await expect(element(by.id('TokenBalanceItem')).atIndex(0)).toBeVisible() }) - describe.each(tokens)( - 'For $symbol', - ({ symbol, tokenId, name, learnMore, actions, moreActions }) => { - it('navigates to asset details on tapping asset', async () => { - await waitForElementByIdAndTap(`TokenBalanceItemTouchable/${tokenId}`) + describe.each(tokens)('For $symbol', ({ symbol, tokenId, learnMore, actions, moreActions }) => { + it('navigates to asset details on tapping asset', async () => { + await waitForElementByIdAndTap(`TokenBalanceItemTouchable/${tokenId}`) + await waitForElementId('TokenDetails/AssetValue') + }) + + if (actions.includes('Send')) { + it('send action navigates to send flow', async () => { + await element(by.id('TokenDetails/Action/Send')).tap() + await validateSendFlow(symbol) await waitForElementId('TokenDetails/AssetValue') }) + } - if (actions.includes('Send')) { - it('send action navigates to send flow', async () => { - await element(by.id('TokenDetails/Action/Send')).tap() - await validateSendFlow(symbol) - await waitForElementId('TokenDetails/AssetValue') - }) - } - - if (actions.includes('Add')) { - it('add action navigates to add cico flow', async () => { - await element(by.id('TokenDetails/Action/Add')).tap() - await validateAddFlow(symbol) - await waitForElementId('TokenDetails/AssetValue') - }) - } - - if (moreActions.includes('Send')) { - it('send action under more actions navigates to send flow', async () => { - await element(by.id('TokenDetails/Action/More')).tap() - await waitForElementByIdAndTap('TokenDetailsMoreActions/Send') - await validateSendFlow(symbol) - await element(by.id('TokenDetailsMoreActions')).swipe('down') - await waitForElementId('TokenDetails/AssetValue') - }) - } - - if (moreActions.includes('Add')) { - it('add action under more actions navigates to add cico flow', async () => { - await element(by.id('TokenDetails/Action/More')).tap() - await waitForElementByIdAndTap('TokenDetailsMoreActions/Add') - await validateAddFlow(symbol) - await element(by.id('TokenDetailsMoreActions')).swipe('down') - await waitForElementId('TokenDetails/AssetValue') - }) - } + if (actions.includes('Add')) { + it('add action navigates to add cico flow', async () => { + await element(by.id('TokenDetails/Action/Add')).tap() + await validateAddFlow(symbol) + await waitForElementId('TokenDetails/AssetValue') + }) + } - if (moreActions.includes('Withdraw')) { - it('withdraw action under more actions navigates to withdraw spend screen', async () => { - await element(by.id('TokenDetails/Action/More')).tap() - await waitForElementByIdAndTap('TokenDetailsMoreActions/Withdraw') - await waitForElementId('FiatExchangeTokenBalance') - await element(by.id('BackChevron')).tap() - await element(by.id('TokenDetailsMoreActions')).swipe('down') - await waitForElementId('TokenDetails/AssetValue') - }) - } + if (moreActions.includes('Send')) { + it('send action under more actions navigates to send flow', async () => { + await element(by.id('TokenDetails/Action/More')).tap() + await waitForElementByIdAndTap('TokenDetailsMoreActions/Send') + await validateSendFlow(symbol) + await element(by.id('TokenDetailsMoreActions')).swipe('down') + await waitForElementId('TokenDetails/AssetValue') + }) + } - if (learnMore) { - it('learn more navigates to coingecko page', async () => { - await scrollIntoView(`Learn more about ${name}`, 'TokenDetailsScrollView') - await waitForElementByIdAndTap('TokenDetails/LearnMore') - await waitForElementId('RNWebView') - await waitFor(element(by.text('www.coingecko.com'))) - .toBeVisible() - .withTimeout(10 * 1000) - await element(by.id('WebViewScreen/CloseButton')).tap() - await waitForElementId('TokenBalanceItem') - }) - } + if (moreActions.includes('Add')) { + it('add action under more actions navigates to add cico flow', async () => { + await element(by.id('TokenDetails/Action/More')).tap() + await waitForElementByIdAndTap('TokenDetailsMoreActions/Add') + await validateAddFlow(symbol) + await element(by.id('TokenDetailsMoreActions')).swipe('down') + await waitForElementId('TokenDetails/AssetValue') + }) + } - it('navigates back to Assets page', async () => { + if (moreActions.includes('Withdraw')) { + it('withdraw action under more actions navigates to withdraw spend screen', async () => { + await element(by.id('TokenDetails/Action/More')).tap() + await waitForElementByIdAndTap('TokenDetailsMoreActions/Withdraw') + await waitForElementId('FiatExchangeTokenBalance') await element(by.id('BackChevron')).tap() - await waitForElementId('Assets/TabBar') + await element(by.id('TokenDetailsMoreActions')).swipe('down') + await waitForElementId('TokenDetails/AssetValue') }) } - ) + + if (learnMore) { + it('learn more navigates to coingecko page', async () => { + await scrollIntoViewByTestId('TokenDetails/LearnMore', 'TokenDetailsScrollView') + await waitForElementByIdAndTap('TokenDetails/LearnMore') + await waitForElementId('RNWebView') + await waitFor(element(by.text('www.coingecko.com'))) + .toBeVisible() + .withTimeout(10 * 1000) + await element(by.id('WebViewScreen/CloseButton')).tap() + await waitForElementId('TokenBalanceItem') + }) + } + + it('navigates back to Assets page', async () => { + await element(by.id('BackChevron')).tap() + await waitForElementId('Assets/TabBar') + }) + }) }) } diff --git a/e2e/src/utils/utils.js b/e2e/src/utils/utils.js index 166848bf0aa..cd52464265c 100644 --- a/e2e/src/utils/utils.js +++ b/e2e/src/utils/utils.js @@ -285,6 +285,22 @@ export async function scrollIntoView(scrollTo, scrollIn, speed = 350, direction } catch {} } +/** + * Scrolls to an element by testID within another + * @param {string} scrollTo - The element to scroll to by testID. + * @param {string} scrollIn - The element to scroll within to by testID. + * @param {number} [speed=350] - The speed at which to scroll + * @param {string} [direction='down'] - The direction of which to scroll + */ +export async function scrollIntoViewByTestId(scrollTo, scrollIn, speed = 350, direction = 'down') { + try { + await waitFor(element(by.id(scrollTo))) + .toBeVisible() + .whileElement(by.id(scrollIn)) + .scroll(speed, direction) + } catch {} +} + export function getDeviceModel() { return device.name.split(/\s(.+)/)[1].replace(/[(]|[)]/g, '') } diff --git a/src/analytics/Events.tsx b/src/analytics/Events.tsx index 718c56eebfc..7beec9a517e 100644 --- a/src/analytics/Events.tsx +++ b/src/analytics/Events.tsx @@ -357,11 +357,7 @@ export enum TransactionEvents { } export enum CeloExchangeEvents { - celo_home_info = 'celo_home_info', - celo_withdraw_completed = 'celo_withdraw_completed', - - celo_chart_tapped = 'celo_chart_tapped', } export enum FiatExchangeEvents { diff --git a/src/analytics/Properties.tsx b/src/analytics/Properties.tsx index 2aeb4fb7769..b1d7ee7d8d6 100644 --- a/src/analytics/Properties.tsx +++ b/src/analytics/Properties.tsx @@ -711,11 +711,9 @@ interface TransactionEventsProperties { } interface CeloExchangeEventsProperties { - [CeloExchangeEvents.celo_home_info]: undefined [CeloExchangeEvents.celo_withdraw_completed]: { amount: string } - [CeloExchangeEvents.celo_chart_tapped]: undefined } interface FiatExchangeEventsProperties { diff --git a/src/analytics/docs.ts b/src/analytics/docs.ts index dc9829f0cdd..474ce147979 100644 --- a/src/analytics/docs.ts +++ b/src/analytics/docs.ts @@ -365,9 +365,7 @@ export const eventDocs: Record = { [TransactionEvents.transaction_confirmed]: `when a transaction is confirmed by the blockchain`, [TransactionEvents.transaction_error]: `when a transaction submission emits an error (only for contract-kit)`, [TransactionEvents.transaction_exception]: `when a transaction submission throws`, - [CeloExchangeEvents.celo_home_info]: `when the (i) next to Celo Gold price is clicked, launching education (not pictured)`, [CeloExchangeEvents.celo_withdraw_completed]: `when the transaction for the withdrawal is completed`, - [CeloExchangeEvents.celo_chart_tapped]: `when user clicks the chart on exchange screen`, // The CICO landing page accessible from the Settings Menu [FiatExchangeEvents.cico_landing_token_balance]: `User taps to view detailed token balance`, @@ -609,6 +607,8 @@ export const eventDocs: Record = { // [CeloExchangeEvents.celo_withdraw_cancel]: `when ’cancel’ is clicked on the review screen`, // [CeloExchangeEvents.celo_withdraw_confirm]: `when ‘withdraw’ is clicked on the review screen`, // [CeloExchangeEvents.celo_withdraw_error]: `when there's an error on the withdrawal transaction`, + // [CeloExchangeEvents.celo_home_info]: `when the (i) next to Celo Gold price is clicked, launching education (not pictured)`, + // [CeloExchangeEvents.celo_chart_tapped]: `when user clicks the chart on exchange screen`, // [NftEvents.nft_gallery_screen_open]: `When the gallery screen is mounted`, // [PhoneVerificationEvents.phone_verification_input_help_skip]: `when the user presses skip on the help dialog to skip verification`, // [PhoneVerificationEvents.phone_verification_skip]: `when skip is pressed in the phone number input screen`, From 05021025f0f88722d893f74fbefa53c67c926614 Mon Sep 17 00:00:00 2001 From: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:48:31 -0600 Subject: [PATCH 14/14] add import --- e2e/src/usecases/Assets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/src/usecases/Assets.js b/e2e/src/usecases/Assets.js index aeb1b230fab..a98d4297aba 100644 --- a/e2e/src/usecases/Assets.js +++ b/e2e/src/usecases/Assets.js @@ -5,7 +5,7 @@ import { quickOnboarding, waitForElementByIdAndTap, waitForElementId, - scrollIntoView, + scrollIntoViewByTestId, } from '../utils/utils' async function validateSendFlow(tokenSymbol) {