11import { CursorProps , GradientProps , SlideAreaChart , ToolTipProps , ToolTipTextRenderersInput , YAxisProps } from '@connectedcars/react-native-slide-charts'
2- import { asArray , asEither , asNumber , asObject , asString , asTuple } from 'cleaners'
2+ import { asArray , asMaybe , asNumber , asObject , asString , asTuple } from 'cleaners'
33import * as React from 'react'
44import { Dimensions , LayoutChangeEvent , Platform , View } from 'react-native'
55import { cacheStyles } from 'react-native-patina'
@@ -14,9 +14,9 @@ import { formatFiatString } from '../../hooks/useFiatText'
1414import { useHandler } from '../../hooks/useHandler'
1515import { formatDate } from '../../locales/intl'
1616import { lstrings } from '../../locales/strings'
17+ import { snooze } from '../../util/utils'
1718import { MinimalButton } from '../buttons/MinimalButton'
1819import { FillLoader } from '../progress-indicators/FillLoader'
19- import { showWarning } from '../services/AirshipInstance'
2020import { Theme , useTheme } from '../services/ThemeContext'
2121import { ReText } from '../text/ReText'
2222import { EdgeText } from '../themed/EdgeText'
@@ -53,8 +53,6 @@ const asCoinGeckoMarketChartRange = asObject<CoinGeckoMarketChartRange>({
5353 total_volumes : asArray ( asCoinGeckoDataPair )
5454} )
5555
56- const asCoinGeckoMarketApi = asEither ( asCoinGeckoMarketChartRange , asCoinGeckoError )
57-
5856const COINGECKO_URL = 'https://api.coingecko.com'
5957const COINGECKO_URL_PRO = 'https://pro-api.coingecko.com'
6058const MARKET_CHART_ENDPOINT_4S = '/api/v3/coins/%1$s/market_chart/range?vs_currency=%2$s&from=%3$s&to=%4$s'
@@ -130,7 +128,7 @@ const reduceChartData = (chartData: ChartDataPoint[], timespan: Timespan): Chart
130128const SwipeChartComponent = ( params : Props ) => {
131129 const theme = useTheme ( )
132130 const styles = getStyles ( theme )
133- const { assetId, currencyCode , fiatCurrencyCode } = params
131+ const { assetId, fiatCurrencyCode } = params
134132
135133 // #region Chart setup
136134
@@ -190,59 +188,57 @@ const SwipeChartComponent = (params: Props) => {
190188 sMinMaxOpacity . value = withDelay ( ANIMATION_DURATION . maxMinFadeInDelay , withTiming ( 1 , { duration : ANIMATION_DURATION . maxMinFadeIn } ) )
191189 }
192190
193- try {
194- if ( cachedChartData != null ) {
195- // The chart price line animation is slow when transitioning directly
196- // between datasets.
197- // Add a delay so the component can get re-mounted with fresh data
198- // instead.
199- setTimeout ( ( ) => {
200- setChartData ( cachedChartData )
201- setIsLoading ( false )
202- delayShowMinMaxLabels ( )
203- } , 10 )
204- } else {
205- const unixNow = Math . trunc ( new Date ( ) . getTime ( ) / 1000 )
206- const fromParam = unixNow - queryFromTimeOffset
207- const fetchPath = sprintf ( MARKET_CHART_ENDPOINT_4S , assetId , fiatCurrencyCode , fromParam , unixNow )
208- // Start with the free base URL
209- let fetchUrl = `${ COINGECKO_URL } ${ fetchPath } `
210- do {
211- // Construct the dataset query
212- const response = await fetch ( fetchUrl )
213- const result = await response . json ( )
214- const marketChartRange = asCoinGeckoMarketApi ( result )
215- if ( 'status' in marketChartRange ) {
216- if ( marketChartRange . status . error_code === 429 ) {
217- // Rate limit error, use our API key as a fallback
218- if ( ! fetchUrl . includes ( 'x_cg_pro_api_key' ) ) {
219- fetchUrl = `${ COINGECKO_URL_PRO } ${ fetchPath } &x_cg_pro_api_key=${ ENV . COINGECKO_API_KEY } `
220- continue
221- }
222- } else {
223- throw new Error ( JSON . stringify ( marketChartRange ) )
191+ if ( cachedChartData != null ) {
192+ // The chart price line animation is slow when transitioning directly
193+ // between datasets.
194+ // Add a delay so the component can get re-mounted with fresh data
195+ // instead.
196+ setTimeout ( ( ) => {
197+ setChartData ( cachedChartData )
198+ setIsLoading ( false )
199+ delayShowMinMaxLabels ( )
200+ } , 10 )
201+ } else {
202+ const unixNow = Math . trunc ( new Date ( ) . getTime ( ) / 1000 )
203+ const fromParam = unixNow - queryFromTimeOffset
204+ const fetchPath = sprintf ( MARKET_CHART_ENDPOINT_4S , assetId , fiatCurrencyCode , fromParam , unixNow )
205+ // Start with the free base URL
206+ let fetchUrl = `${ COINGECKO_URL } ${ fetchPath } `
207+ do {
208+ // Construct the dataset query
209+ const response = await fetch ( fetchUrl )
210+ const result = await response . json ( )
211+ const apiError = asMaybe ( asCoinGeckoError ) ( result )
212+ if ( apiError != null ) {
213+ if ( apiError . status . error_code === 429 ) {
214+ // Rate limit error, use our API key as a fallback
215+ if ( ! fetchUrl . includes ( 'x_cg_pro_api_key' ) && ENV . COINGECKO_API_KEY !== '' ) {
216+ fetchUrl = `${ COINGECKO_URL_PRO } ${ fetchPath } &x_cg_pro_api_key=${ ENV . COINGECKO_API_KEY } `
224217 }
225- } else {
226- const rawChartData = marketChartRange . prices . map ( rawDataPoint => {
227- return {
228- x : new Date ( rawDataPoint [ 0 ] ) ,
229- y : rawDataPoint [ 1 ]
230- }
231- } )
232- const reducedChartData = reduceChartData ( rawChartData , selectedTimespan )
233-
234- setChartData ( reducedChartData )
235- cachedTimespanChartData . set ( selectedTimespan , reducedChartData )
236- setCachedChartData ( cachedTimespanChartData )
237- setIsLoading ( false )
238- delayShowMinMaxLabels ( )
239- break
218+ // Wait 2 second before retrying. It typically takes 1 minute
219+ // before rate limiting is relieved, so even 2 seconds is hasty.
220+ await snooze ( 2000 )
221+ continue
222+ }
223+ throw new Error ( `Failed to fetch market data: ${ apiError . status . error_code } ${ apiError . status . error_message } ` )
224+ }
225+
226+ const marketChartRange = asCoinGeckoMarketChartRange ( result )
227+ const rawChartData = marketChartRange . prices . map ( rawDataPoint => {
228+ return {
229+ x : new Date ( rawDataPoint [ 0 ] ) ,
230+ y : rawDataPoint [ 1 ]
240231 }
241- } while ( true )
242- }
243- } catch ( e : any ) {
244- showWarning ( `Failed to retrieve market data for ${ currencyCode } .` )
245- console . error ( JSON . stringify ( e ) )
232+ } )
233+ const reducedChartData = reduceChartData ( rawChartData , selectedTimespan )
234+
235+ setChartData ( reducedChartData )
236+ cachedTimespanChartData . set ( selectedTimespan , reducedChartData )
237+ setCachedChartData ( cachedTimespanChartData )
238+ setIsLoading ( false )
239+ delayShowMinMaxLabels ( )
240+ break
241+ } while ( true )
246242 }
247243 }
248244 // eslint-disable-next-line react-hooks/exhaustive-deps
0 commit comments