Skip to content

Commit

Permalink
feat: earn transaction feed items (#5414)
Browse files Browse the repository at this point in the history
### Description

Adds the `EarnFeedItem.tsx`, types and associated GraphQL queries to
populate the transaction feed.

#### Screenshots

| iOS transaction Feed | iOS Details |
| ----- | ----- |
|
![](https://github.com/valora-inc/wallet/assets/26950305/52a3099b-5ff1-4c65-bafd-eccc1c44cabe
"iOS Feed") |
![](https://github.com/valora-inc/wallet/assets/26950305/9a7d6598-c3e8-4ee5-9007-95b68afc318c
"iOS Details") |

### Test plan

- [x] Tested locally on iOS
- [x] Tested locally on Android
- [x] Unit tests added

### Related issues

- Fixes ACT-1182

### Backwards compatibility

Yes
### Network scalability

If a new NetworkId and/or Network are added in the future, the changes
in this PR will:

- [x] Continue to work without code changes, OR trigger a compilation
error (guaranteeing we find it when a new network is added)
  • Loading branch information
MuckT committed May 17, 2024
1 parent 2666d6c commit 64fd9eb
Show file tree
Hide file tree
Showing 13 changed files with 775 additions and 4 deletions.
8 changes: 8 additions & 0 deletions locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2398,6 +2398,14 @@
"cta": "Collect Earnings",
"errorTitle": "Something went wrong",
"errorDescription": "Unable to fetch deposit details. Please try again."
},
"transactionFeed": {
"earnClaimTitle": "Collected",
"earnClaimSubtitle": "from {{providerName}} Pool",
"earnDepositTitle": "Deposited",
"earnDepositSubtitle": "to {{providerName}} Pool",
"earnWithdrawTitle": "Withdrew",
"earnWithdrawSubtitle": "from {{providerName}} Pool"
}
}
}
1 change: 1 addition & 0 deletions src/analytics/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -690,4 +690,5 @@ export enum EarnEvents {
earn_deposit_submit_cancel = 'earn_deposit_submit_cancel',
earn_view_pools_press = 'earn_view_pools_press',
earn_exit_pool_press = 'earn_exit_pool_press',
earn_feed_item_select = 'earn_feed_item_select',
}
3 changes: 3 additions & 0 deletions src/analytics/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1605,6 +1605,9 @@ interface EarnEventsProperties {
tokenAmount: string
providerId: string
}
[EarnEvents.earn_feed_item_select]: {
origin: 'EarnDeposit' | 'EarnWithdraw' | 'EarnClaimReward'
}
}

export type AnalyticsPropertiesList = AppEventsProperties &
Expand Down
1 change: 1 addition & 0 deletions src/analytics/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
[EarnEvents.earn_deposit_submit_cancel]: `When the user cancels the deposit after submitting by cancelling PIN input`,
[EarnEvents.earn_view_pools_press]: `When the user taps on the view pools button from token details`,
[EarnEvents.earn_exit_pool_press]: `When the user taps on the exit pool button from the earn card in discover tab`,
[EarnEvents.earn_feed_item_select]: `When the users taps on an earn transaction feed item`,

// Legacy event docs
// The below events had docs, but are no longer produced by the latest app version.
Expand Down
5 changes: 3 additions & 2 deletions src/icons/UpwardGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import colors from 'src/styles/colors'
interface Props {
color?: string
testID?: string
size?: number
}

function UpwardGraph({ color = colors.primary, testID = 'UpwardGraph' }: Props) {
function UpwardGraph({ color = colors.primary, testID = 'UpwardGraph', size = 16 }: Props) {
return (
<Svg width={16} height={16} viewBox="0 0 16 16" fill="none" testID={testID}>
<Svg width={size} height={size} viewBox="0 0 16 16" fill="none" testID={testID}>
<Path
d="M6.09245 7.91311L2.846 11.1596L2.70687 11.0204L6.44624 7.27601L8.49245 9.32222L8.846 9.67577L9.19955 9.32222L12.1276 6.39422L12.4811 6.04066L12.1276 5.68711L11.6071 5.16666H13.5V7.05956L12.9796 6.53911L12.6263 6.18584L12.2727 6.53883L8.84628 9.95984L6.79955 7.91311L6.446 7.55956L6.09245 7.91311Z"
fill={color}
Expand Down
206 changes: 206 additions & 0 deletions src/transactions/feed/EarnFeedItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { fireEvent, render, within } from '@testing-library/react-native'
import React from 'react'
import { Provider } from 'react-redux'
import { EarnEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import EarnFeedItem from 'src/transactions/feed/EarnFeedItem'
import { NetworkId, TransactionStatus } from 'src/transactions/types'
import networkConfig from 'src/web3/networkConfig'
import { createMockStore } from 'test/utils'
import { mockAaveArbUsdcAddress, mockArbArbTokenId } from 'test/values'

const store = createMockStore({
tokens: {
tokenBalances: {
[networkConfig.arbUsdcTokenId]: {
tokenId: networkConfig.arbUsdcTokenId,
symbol: 'USDC',
priceUsd: '1',
priceFetchedAt: Date.now(),
networkId: NetworkId['arbitrum-sepolia'],
},
[mockArbArbTokenId]: {
tokenId: mockArbArbTokenId,
symbol: 'ARB',
priceUsd: '0.9898',
priceFetchedAt: Date.now(),
networkId: NetworkId['arbitrum-sepolia'],
},
[networkConfig.aaveArbUsdcTokenId]: {
networkId: NetworkId['arbitrum-sepolia'],
address: mockAaveArbUsdcAddress,
tokenId: networkConfig.aaveArbUsdcTokenId,
symbol: 'aArbSepUSDC',
priceUsd: '1',
priceFetchedAt: Date.now(),
},
},
},
})

describe.each([
{
type: 'EarnWithdraw',
transaction: {
__typename: 'EarnWithdraw',
inAmount: {
localAmount: null,
tokenAddress: '0xdef',
tokenId: networkConfig.arbUsdcTokenId,
value: '1',
},
outAmount: {
localAmount: null,
tokenAddress: mockAaveArbUsdcAddress,
tokenId: networkConfig.aaveArbUsdcTokenId,
value: '0.996614',
},
block: '211276583',
fees: [
{
amount: {
localAmount: null,
tokenAddress: null,
tokenId: mockArbArbTokenId,
value: '0.00000229122',
},
type: 'SECURITY_FEE',
},
],
networkId: NetworkId['arbitrum-sepolia'],
providerId: 'aave-v3',
timestamp: Date.now(),
transactionHash: '0xHASH0',
type: 'EARN_WITHDRAW',
status: TransactionStatus.Complete,
},
expectedTitle: 'earnFlow.transactionFeed.earnWithdrawTitle',
expectedSubTitle: 'earnFlow.transactionFeed.earnWithdrawSubtitle, {"providerName":"Aave"}',
expectedTotal: '+1.00 USDC',
expectedTotalLocal: '₱1.33',
},
{
type: 'EarnDeposit',
transaction: {
__typename: 'EarnDeposit',
inAmount: {
localAmount: null,
tokenAddress: mockAaveArbUsdcAddress,
tokenId: networkConfig.aaveArbUsdcTokenId,
value: '10',
},
outAmount: {
localAmount: null,
tokenAddress: '0xdef',
tokenId: networkConfig.arbUsdcTokenId,
value: '10',
},
block: '210927567',
fees: [
{
amount: {
localAmount: null,
tokenAddress: null,
tokenId: mockArbArbTokenId,
value: '0.00000284243',
},
type: 'SECURITY_FEE',
},
],
networkId: NetworkId['arbitrum-sepolia'],
providerId: 'aave-v3',
timestamp: Date.now(),
transactionHash: '0xHASH1',
type: 'EARN_DEPOSIT',
status: TransactionStatus.Complete,
},
expectedTitle: 'earnFlow.transactionFeed.earnDepositTitle',
expectedSubTitle: 'earnFlow.transactionFeed.earnDepositSubtitle, {"providerName":"Aave"}',
expectedTotal: '-10.00 USDC',
expectedTotalLocal: '₱13.30',
},
{
type: 'EarnClaimReward',
transaction: {
__typename: 'EarnClaimReward',
amount: {
localAmount: null,
tokenAddress: '0xhij',
tokenId: mockArbArbTokenId,
value: '1.5',
},
block: '211278852',
fees: [
{
amount: {
localAmount: null,
tokenAddress: null,
tokenId: mockArbArbTokenId,
value: '0.00000146037',
},
type: 'SECURITY_FEE',
},
],
networkId: NetworkId['arbitrum-sepolia'],
providerId: 'aave-v3',
timestamp: Date.now(),
transactionHash: '0xHASH2',
type: 'EARN_CLAIM_REWARD',
status: TransactionStatus.Complete,
} as any,
expectedTitle: 'earnFlow.transactionFeed.earnClaimTitle',
expectedSubTitle: 'earnFlow.transactionFeed.earnClaimSubtitle, {"providerName":"Aave"}',
expectedTotal: '+1.50 ARB',
expectedTotalLocal: '₱1.97',
},
])(
`$type`,
({ type, transaction, expectedTitle, expectedSubTitle, expectedTotal, expectedTotalLocal }) => {
beforeEach(() => {
jest.clearAllMocks()
})

it('Should render correctly', () => {
const { getByText, getByTestId } = render(
<Provider store={store}>
<EarnFeedItem transaction={transaction} />
</Provider>
)

expect(getByText(expectedTitle)).toBeTruthy()
expect(getByText(expectedSubTitle)).toBeTruthy()
expect(
within(getByTestId(`EarnFeedItem/${type}-amount-crypto`)).getByText(expectedTotal)
).toBeTruthy()
expect(
within(getByTestId(`EarnFeedItem/${type}-amount-local`)).getByText(expectedTotalLocal)
).toBeTruthy()
})

it('Should navigate correctly on tap', () => {
const { getByTestId } = render(
<Provider store={store}>
<EarnFeedItem transaction={transaction} />
</Provider>
)

fireEvent.press(getByTestId(`EarnFeedItem/${transaction.transactionHash}`))
expect(navigate).toHaveBeenCalledWith(Screens.TransactionDetailsScreen, { transaction })
})

it('Should fire analytic event on tap', () => {
const { getByTestId } = render(
<Provider store={store}>
<EarnFeedItem transaction={transaction} />
</Provider>
)

fireEvent.press(getByTestId(`EarnFeedItem/${transaction.transactionHash}`))
expect(ValoraAnalytics.track).toHaveBeenCalledWith(EarnEvents.earn_feed_item_select, {
origin: type,
})
})
}
)
Loading

0 comments on commit 64fd9eb

Please sign in to comment.