Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(assets): add chart to token details screen #4283

Merged
merged 3 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions src/exchange/CeloGoldHistoryChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
import _ from 'lodash'
import React, { useCallback, useState } from 'react'
import { WithTranslation } from 'react-i18next'
import { ActivityIndicator, LayoutChangeEvent, StyleSheet, Text, View } from 'react-native'
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'
Expand All @@ -24,10 +31,12 @@
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] }
const CHART_PADDING = { left: variables.contentPadding, right: variables.contentPadding }

interface OwnProps {
testID?: string
color?: colors
containerStyle?: ViewStyle
chartPadding?: number
}

type Props = WithTranslation & OwnProps
Expand Down Expand Up @@ -92,7 +101,8 @@

function renderPointOnChart(
chartData: Array<{ amount: number | BigNumber; displayValue: string }>,
chartWidth: number
chartWidth: number,
color: colors
) {
let lowestRateIdx = 0,
highestRateIdx = 0
Expand All @@ -112,15 +122,15 @@
result.push(
<G key={idx + 'dot'}>
<Line x1={0} y1={y} x2={chartWidth} y2={y} stroke={colors.gray2} strokeWidth="1" />
<Circle cx={x} cy={y} r="4" fill={colors.goldUI} />
<Circle cx={x} cy={y} r="4" fill={color} />
</G>
)
break

case chartData.length - 1:
result.push(
<G key={idx + 'dot'}>
<Circle cx={x} cy={y} r="4" fill={colors.goldUI} />
<Circle cx={x} cy={y} r="4" fill={color} />
</G>
)
break
Expand Down Expand Up @@ -164,15 +174,21 @@
}
}

function Loader() {
function Loader({ color }: { color: colors }) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color={colors.goldUI} />
<ActivityIndicator size="large" color={color} />
</View>
)
}

function CeloGoldHistoryChart({ testID, i18n }: Props) {
function CeloGoldHistoryChart({
testID,
i18n,
color = colors.goldUI,
containerStyle,
chartPadding = variables.contentPadding,
}: Props) {
const localCurrencyCode = useSelector(getLocalCurrencyCode)
const displayLocalCurrency = useCallback(
(amount: BigNumber.Value) =>
Expand All @@ -193,7 +209,7 @@
}, [])

if (!exchangeHistory.aggregatedExchangeRates?.length) {
return <Loader />
return <Loader color={color} />
}

const chartData = exchangeHistory.aggregatedExchangeRates.map((exchangeRate) => {
Expand All @@ -210,7 +226,7 @@
null
const oldestGoldRateInLocalCurrency = chartData[0].amount
if (oldestGoldRateInLocalCurrency === null || currentGoldRateInLocalCurrency === null) {
return <Loader />
return <Loader color={color} />

Check warning on line 229 in src/exchange/CeloGoldHistoryChart.tsx

View check run for this annotation

Codecov / codecov/patch

src/exchange/CeloGoldHistoryChart.tsx#L229

Added line #L229 was not covered by tests
}
// 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,
Expand All @@ -219,7 +235,7 @@
amount: currentGoldRateInLocalCurrency,
displayValue: displayLocalCurrency(currentGoldRateInLocalCurrency),
})
const RenderPoint = renderPointOnChart(chartData, CHART_WIDTH)
const RenderPoint = renderPointOnChart(chartData, CHART_WIDTH, color)

const values = chartData.map((el) => el.amount)
const min = Math.min(...values)
Expand All @@ -236,11 +252,11 @@
const latestExchangeRate = _.last(exchangeHistory.aggregatedExchangeRates)!

return (
<View style={styles.container} onTouchStart={onTap} testID={testID}>
<View style={[styles.container, containerStyle]} onTouchStart={onTap} testID={testID}>
<VictoryGroup
domainPadding={CHART_DOMAIN_PADDING}
singleQuadrantDomainPadding={false}
padding={CHART_PADDING}
padding={{ left: chartPadding, right: chartPadding }}
width={CHART_WIDTH}
height={CHART_HEIGHT}
data={chartData.map((el) => el.amount)}
Expand All @@ -252,11 +268,11 @@
<VictoryLine
interpolation="monotoneX"
style={{
data: { stroke: colors.goldUI },
data: { stroke: color },
}}
/>
</VictoryGroup>
<View style={styles.range}>
<View style={[styles.range, { paddingHorizontal: chartPadding }]}>
<Text style={styles.timeframe}>
{formatFeedDate(latestExchangeRate.timestamp - exchangeHistory.range, i18n)}
</Text>
Expand All @@ -281,7 +297,6 @@
fontSize: 16,
},
range: {
paddingHorizontal: variables.contentPadding,
marginTop: variables.contentPadding,
justifyContent: 'space-between',
flexDirection: 'row',
Expand Down
50 changes: 32 additions & 18 deletions src/exchange/__snapshots__/CeloGoldHistoryChart.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ exports[`renders properly 1`] = `
<View
onTouchStart={[Function]}
style={
{
"marginBottom": 24,
}
[
{
"marginBottom": 24,
},
undefined,
]
}
testID="SnapshotCeloGoldOverview"
>
Expand Down Expand Up @@ -164,12 +167,16 @@ exports[`renders properly 1`] = `
</View>
<View
style={
{
"flexDirection": "row",
"justifyContent": "space-between",
"marginTop": 16,
"paddingHorizontal": 16,
}
[
{
"flexDirection": "row",
"justifyContent": "space-between",
"marginTop": 16,
},
{
"paddingHorizontal": 16,
},
]
}
>
<Text
Expand Down Expand Up @@ -200,9 +207,12 @@ exports[`renders while update is in progress 1`] = `
<View
onTouchStart={[Function]}
style={
{
"marginBottom": 24,
}
[
{
"marginBottom": 24,
},
undefined,
]
}
testID="SnapshotCeloGoldOverview"
>
Expand Down Expand Up @@ -349,12 +359,16 @@ exports[`renders while update is in progress 1`] = `
</View>
<View
style={
{
"flexDirection": "row",
"justifyContent": "space-between",
"marginTop": 16,
"paddingHorizontal": 16,
}
[
{
"flexDirection": "row",
"justifyContent": "space-between",
"marginTop": 16,
},
{
"paddingHorizontal": 16,
},
]
}
>
<Text
Expand Down
29 changes: 28 additions & 1 deletion src/tokens/TokenDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import TokenDetailsScreen from 'src/tokens/TokenDetails'
import { ONE_DAY_IN_MILLIS } from 'src/utils/time'
import MockedNavigator from 'test/MockedNavigator'
import { createMockStore } from 'test/utils'
import { mockCeloTokenId, mockPoofTokenId, mockTokenBalances } from 'test/values'
import {
exchangePriceHistory,
mockCeloTokenId,
mockPoofTokenId,
mockTokenBalances,
} from 'test/values'

jest.mock('src/statsig', () => ({
getDynamicConfigParams: jest.fn(() => {
Expand Down Expand Up @@ -41,6 +46,7 @@ describe('TokenDetails', () => {
expect(getByText('tokenDetails.yourBalance')).toBeTruthy()
expect(getByTestId('TokenBalanceItem')).toBeTruthy()
expect(queryByTestId('TokenDetails/LearnMore')).toBeFalsy()
expect(queryByTestId('TokenDetails/Chart')).toBeFalsy()
})

it('renders learn more if token has infoUrl', () => {
Expand Down Expand Up @@ -161,6 +167,27 @@ describe('TokenDetails', () => {
expect(queryByText('tokenDetails.priceUnavailable')).toBeFalsy()
})

it('renders chart if token is native (celo)', () => {
const store = createMockStore({
tokens: {
tokenBalances: {
[mockCeloTokenId]: mockTokenBalances[mockCeloTokenId],
},
},
exchange: {
history: exchangePriceHistory,
},
})

const { getByTestId } = render(
<Provider store={store}>
<MockedNavigator component={TokenDetailsScreen} params={{ tokenId: mockCeloTokenId }} />
</Provider>
)

expect(getByTestId('TokenDetails/Chart')).toBeTruthy()
})

it('renders send action only if token has balance, is not swappable and not a CICO token', () => {
const store = createMockStore({
tokens: {
Expand Down
25 changes: 20 additions & 5 deletions src/tokens/TokenDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import TokenDisplay from 'src/components/TokenDisplay'
import TokenIcon, { IconSize } from 'src/components/TokenIcon'
import Touchable from 'src/components/Touchable'
import { TOKEN_MIN_AMOUNT } from 'src/config'
import CeloGoldHistoryChart from 'src/exchange/CeloGoldHistoryChart'
import { CICOFlow } from 'src/fiatExchanges/utils'
import ArrowRightThick from 'src/icons/ArrowRightThick'
import DataDown from 'src/icons/DataDown'
Expand Down Expand Up @@ -77,9 +78,17 @@ export default function TokenDetailsScreen({ route }: Props) {
testID="TokenDetails/Balance"
/>
{!token.isStableCoin && <PriceInfo token={token} />}
{token.isNative && token.symbol === 'CELO' && (
<CeloGoldHistoryChart
color={Colors.dark}
containerStyle={styles.chartContainer}
chartPadding={Spacing.Thick24}
testID="TokenDetails/Chart"
/>
)}
<Actions token={token} />
<Text style={styles.yourBalance}>{t('tokenDetails.yourBalance')}</Text>
<TokenBalanceItem token={token} containerStyle={styles.tokenBalanceItem} />
<TokenBalanceItem token={token} />
{token.infoUrl && (
<LearnMore
tokenName={token.name}
Expand Down Expand Up @@ -273,12 +282,12 @@ function LearnMore({
const styles = StyleSheet.create({
container: {
flex: 1,
marginHorizontal: Spacing.Thick24,
},
titleContainer: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: Spacing.Smallest8,
marginHorizontal: Spacing.Thick24,
},
tokenName: {
...typeScale.labelLarge,
Expand All @@ -290,11 +299,17 @@ const styles = StyleSheet.create({
balance: {
...typeScale.titleLarge,
color: Colors.dark,
marginHorizontal: Spacing.Thick24,
},
chartContainer: {
marginTop: 40,
marginBottom: 0,
},
actions: {
flexDirection: 'row',
marginTop: 40,
marginBottom: Spacing.Regular16,
marginHorizontal: Spacing.Thick24,
},
actionButton: {
flexGrow: 1,
Expand All @@ -310,14 +325,13 @@ const styles = StyleSheet.create({
...typeScale.labelMedium,
color: Colors.dark,
marginTop: Spacing.Regular16,
},
tokenBalanceItem: {
marginHorizontal: 0,
marginHorizontal: Spacing.Thick24,
},
learnMoreContainer: {
borderTopColor: Colors.gray2,
borderTopWidth: 1,
paddingTop: Spacing.Regular16,
marginHorizontal: Spacing.Thick24,
},
learnMoreTouchableContainer: {
flexDirection: 'row',
Expand All @@ -330,6 +344,7 @@ const styles = StyleSheet.create({
},
priceInfo: {
marginTop: Spacing.Tiny4,
marginHorizontal: Spacing.Thick24,
},
priceInfoText: {
...typeScale.labelSmall,
Expand Down
1 change: 1 addition & 0 deletions test/values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ export const mockTokenBalances: Record<string, StoredTokenBalance> = {
showZeroBalance: true,
isCashInEligible: true,
isCashOutEligible: true,
isNative: true,
},
[mockCrealTokenId]: {
priceUsd: '0.17',
Expand Down