Skip to content

Commit 2a68163

Browse files
authored
fix(points): hide live links when user has no jumpstart tokens (#5751)
### Description Hide live links activity when a user has no jumpstart tokens (or live links are disabled). This is consistent with the send flow behavior when the live link option is not shown when a user has no jumpstart tokens. If the user tries to create a jumpstart link without having the jumpstart tokens the app will crash. Context: https://valora-app.slack.com/archives/C04B61SJ6DS/p1723075264724759 ### Test plan * Tested manually * Updated unit test ### Related issues NA ### Backwards compatibility Y ### Network scalability If a new NetworkId and/or Network are added in the future, the changes in this PR will: - [ ] Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added)
1 parent 4b1dfda commit 2a68163

File tree

3 files changed

+134
-8
lines changed

3 files changed

+134
-8
lines changed

src/points/PointsHome.test.tsx

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import { fireEvent, render, waitFor } from '@testing-library/react-native'
22
import * as React from 'react'
33
import { Provider } from 'react-redux'
4-
import { PointsEvents } from 'src/analytics/Events'
54
import AppAnalytics from 'src/analytics/AppAnalytics'
5+
import { PointsEvents } from 'src/analytics/Events'
66
import { navigate } from 'src/navigator/NavigationService'
77
import { Screens } from 'src/navigator/Screens'
88
import PointsHome from 'src/points/PointsHome'
99
import { getHistoryStarted, getPointsConfigRetry } from 'src/points/slice'
1010
import { RootState } from 'src/redux/store'
11+
import { getDynamicConfigParams, getFeatureGate } from 'src/statsig'
12+
import { NetworkId } from 'src/transactions/types'
1113
import { RecursivePartial, createMockStore, getMockStackScreenProps } from 'test/utils'
1214

15+
jest.mock('src/statsig')
1316
jest.mock('src/points/PointsHistoryBottomSheet')
1417

1518
const mockScreenProps = () => getMockStackScreenProps(Screens.PointsHome)
@@ -36,6 +39,16 @@ const renderPointsHome = (storeOverrides?: RecursivePartial<RootState>) => {
3639
},
3740
pointsConfigStatus: 'success',
3841
},
42+
tokens: {
43+
tokenBalances: {
44+
['celo-alfajores:0xusd']: {
45+
tokenId: 'celo-alfajores:0xabcd',
46+
address: '0xabcd',
47+
networkId: NetworkId['celo-alfajores'],
48+
balance: '10',
49+
},
50+
},
51+
},
3952
}
4053
)
4154
const tree = render(
@@ -53,6 +66,10 @@ const renderPointsHome = (storeOverrides?: RecursivePartial<RootState>) => {
5366
describe(PointsHome, () => {
5467
beforeEach(() => {
5568
jest.clearAllMocks()
69+
jest.mocked(getFeatureGate).mockReturnValue(true)
70+
jest
71+
.mocked(getDynamicConfigParams)
72+
.mockReturnValue({ jumpstartContracts: { 'celo-alfajores': '0x1234' } })
5673
})
5774

5875
it('renders a loading state while loading config', async () => {

src/points/selectors.test.ts

+91
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { pointsActivitiesSelector, pointsHistorySelector } from 'src/points/selectors'
2+
import { getDynamicConfigParams, getFeatureGate } from 'src/statsig'
3+
import { NetworkId } from 'src/transactions/types'
24
import { getMockStoreData } from 'test/utils'
35

6+
jest.mock('src/statsig')
7+
48
describe('pointsHistorySelector', () => {
59
it('returns UNIX timestamp', () => {
610
const stateWithPointsHistory = getMockStoreData({
@@ -27,6 +31,12 @@ describe('pointsHistorySelector', () => {
2731
})
2832

2933
describe('pointsActivitiesSelector', () => {
34+
beforeEach(() => {
35+
jest
36+
.mocked(getDynamicConfigParams)
37+
.mockReturnValue({ jumpstartContracts: { 'celo-alfajores': '0x1234' } })
38+
})
39+
3040
it('should return an empty array if there are no activities', () => {
3141
const stateWithoutPointsConfig = getMockStoreData({
3242
points: {
@@ -61,4 +71,85 @@ describe('pointsActivitiesSelector', () => {
6171
{ activityId: 'create-wallet', pointsAmount: 10, completed: true },
6272
])
6373
})
74+
75+
it('should return points activities with live links when enabled and user has jumpstart tokens', () => {
76+
jest.mocked(getFeatureGate).mockReturnValue(true)
77+
78+
const stateWithPointsConfig = getMockStoreData({
79+
points: {
80+
pointsConfig: {
81+
activitiesById: {
82+
'create-live-link': { pointsAmount: 10 },
83+
},
84+
},
85+
},
86+
tokens: {
87+
tokenBalances: {
88+
['celo-alfajores:0xusd']: {
89+
tokenId: 'celo-alfajores:0xabcd',
90+
address: '0xabcd',
91+
networkId: NetworkId['celo-alfajores'],
92+
balance: '10',
93+
},
94+
},
95+
},
96+
})
97+
const result = pointsActivitiesSelector(stateWithPointsConfig)
98+
99+
expect(result).toEqual([{ activityId: 'create-live-link', pointsAmount: 10, completed: false }])
100+
})
101+
102+
it('should return points activities without live links if they are disabled', () => {
103+
jest.mocked(getFeatureGate).mockReturnValue(false)
104+
105+
const stateWithPointsConfig = getMockStoreData({
106+
points: {
107+
pointsConfig: {
108+
activitiesById: {
109+
'create-live-link': { pointsAmount: 10 },
110+
},
111+
},
112+
},
113+
tokens: {
114+
tokenBalances: {
115+
['celo-alfajores:0xusd']: {
116+
tokenId: 'celo-alfajores:0xabcd',
117+
address: '0xabcd',
118+
networkId: NetworkId['celo-alfajores'],
119+
balance: '10',
120+
},
121+
},
122+
},
123+
})
124+
const result = pointsActivitiesSelector(stateWithPointsConfig)
125+
126+
expect(result).toEqual([])
127+
})
128+
129+
it('should return points activities without live links if user has no jumpstart tokens', () => {
130+
jest.mocked(getFeatureGate).mockReturnValue(true)
131+
132+
const stateWithPointsConfig = getMockStoreData({
133+
points: {
134+
pointsConfig: {
135+
activitiesById: {
136+
'create-live-link': { pointsAmount: 10 },
137+
},
138+
},
139+
},
140+
tokens: {
141+
tokenBalances: {
142+
['celo-alfajores:0xusd']: {
143+
tokenId: 'celo-alfajores:0xabcd',
144+
address: '0xabcd',
145+
networkId: NetworkId['celo-alfajores'],
146+
balance: '0',
147+
},
148+
},
149+
},
150+
})
151+
const result = pointsActivitiesSelector(stateWithPointsConfig)
152+
153+
expect(result).toEqual([])
154+
})
64155
})

src/points/selectors.ts

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { createSelector } from 'reselect'
22
import { ClaimHistoryCardItem, PointsActivity, PointsActivityId } from 'src/points/types'
33
import { RootState } from 'src/redux/reducers'
4+
import { getFeatureGate } from 'src/statsig'
5+
import { StatsigFeatureGates } from 'src/statsig/types'
6+
import { jumpstartSendTokensSelector } from 'src/tokens/selectors'
47

58
export const nextPageUrlSelector = (state: RootState) => {
69
return state.points.nextPageUrl
@@ -35,14 +38,29 @@ export const pointsConfigStatusSelector = (state: RootState) => state.points.poi
3538

3639
const pointsConfigSelector = (state: RootState) => state.points.pointsConfig
3740

41+
const showJumpstartSendSelector = () => getFeatureGate(StatsigFeatureGates.SHOW_JUMPSTART_SEND)
42+
3843
export const pointsActivitiesSelector = createSelector(
39-
[pointsConfigSelector, trackOnceActivitiesSelector],
40-
(pointsConfig, trackOnceActivities) => {
41-
return Object.entries(pointsConfig.activitiesById).map(([activityId, metadata]) => ({
42-
...metadata,
43-
activityId,
44-
completed: trackOnceActivities[activityId as PointsActivityId] ?? false,
45-
})) as PointsActivity[]
44+
[
45+
pointsConfigSelector,
46+
trackOnceActivitiesSelector,
47+
jumpstartSendTokensSelector,
48+
showJumpstartSendSelector,
49+
],
50+
(pointsConfig, trackOnceActivities, jumpstartTokens, jumpstartSendEnabled) => {
51+
const showJumpstart = jumpstartSendEnabled && jumpstartTokens.length > 0
52+
const excludedActivities = new Set<PointsActivityId>()
53+
if (!showJumpstart) {
54+
excludedActivities.add('create-live-link')
55+
}
56+
57+
return (
58+
Object.entries(pointsConfig.activitiesById).map(([activityId, metadata]) => ({
59+
...metadata,
60+
activityId,
61+
completed: trackOnceActivities[activityId as PointsActivityId] ?? false,
62+
})) as PointsActivity[]
63+
).filter(({ activityId }) => !excludedActivities.has(activityId))
4664
}
4765
)
4866

0 commit comments

Comments
 (0)