Skip to content

Commit

Permalink
chore: enable restricting supercharge promos (#4303)
Browse files Browse the repository at this point in the history
### Description

The needed logic is described in the linear task.

### Test plan

Updated unit tests, manual testing

### Related issues

- Fixes RET-889

### Backwards compatibility

Y
  • Loading branch information
kathaypacific authored Oct 12, 2023
1 parent ba898a2 commit 2c4c767
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 25 deletions.
28 changes: 26 additions & 2 deletions src/consumerIncentives/ConsumerIncentivesHomeScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ReactTestInstance } from 'react-test-renderer'
import { MockStoreEnhanced } from 'redux-mock-store'
import { SUPERCHARGE_LEARN_MORE } from 'src/config'
import ConsumerIncentivesHomeScreen from 'src/consumerIncentives/ConsumerIncentivesHomeScreen'
import { initialState, State } from 'src/consumerIncentives/slice'
import { State, initialState } from 'src/consumerIncentives/slice'
import {
ONE_CUSD_REWARD_RESPONSE,
ONE_CUSD_REWARD_RESPONSE_V2,
Expand All @@ -15,11 +15,13 @@ import { FiatExchangeFlow } from 'src/fiatExchanges/utils'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { RootState } from 'src/redux/reducers'
import { getFeatureGate } from 'src/statsig'
import { StoredTokenBalance } from 'src/tokens/slice'
import { NetworkId } from 'src/transactions/types'
import { createMockStore } from 'test/utils'
import { mockCeurAddress, mockCusdAddress, mockCusdTokenId } from 'test/values'
import { NetworkId } from 'src/transactions/types'

jest.mock('src/statsig')
interface TokenBalances {
[address: string]: StoredTokenBalance
}
Expand Down Expand Up @@ -337,4 +339,26 @@ describe('ConsumerIncentivesHomeScreen', () => {
}
`)
})

it('hides the disclaimer and CTA once a user has claimed rewards in a restricted environment', () => {
jest.mocked(getFeatureGate).mockReturnValue(true)

const { queryByTestId, queryByText } = render(
<Provider
store={createStore({
numberVerified: true,
tokenBalances: ONLY_CUSD_BALANCE,
supercharge: {
...initialState,
availableRewards: [],
},
})}
>
<ConsumerIncentivesHomeScreen />
</Provider>
)

expect(queryByTestId('ConsumerIncentives/CTA')).toBeFalsy()
expect(queryByText('superchargeDisclaimer')).toBeFalsy()
})
})
46 changes: 29 additions & 17 deletions src/consumerIncentives/ConsumerIncentivesHomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import { navigate, navigateBack } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { userLocationDataSelector } from 'src/networkInfo/selectors'
import useSelector from 'src/redux/useSelector'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import colors from 'src/styles/colors'
import fontStyles from 'src/styles/fonts'
import variables from 'src/styles/variables'
Expand Down Expand Up @@ -236,6 +238,10 @@ export default function ConsumerIncentivesHomeScreen() {
dispatch(fetchAvailableRewards())
}, [])

const restrictSuperchargeForClaimOnly = getFeatureGate(
StatsigFeatureGates.RESTRICT_SUPERCHARGE_FOR_CLAIM_ONLY
)

const userIsVerified = useSelector(phoneNumberVerifiedSelector)
const { hasBalanceForSupercharge, superchargingTokenConfig, hasMaxBalance } =
useSelector(superchargeInfoSelector)
Expand Down Expand Up @@ -297,30 +303,36 @@ export default function ConsumerIncentivesHomeScreen() {
</Trans>
) : hasMaxBalance ? (
t('superchargeDisclaimerMaxRewards', { token: superchargingTokenConfig?.tokenSymbol })
) : restrictSuperchargeForClaimOnly ? (
''
) : (
t('superchargeDisclaimer', {
amount: tokenConfigToSupercharge.maxBalance,
token: tokenConfigToSupercharge.tokenSymbol,
})
)}
</Text>
<View style={styles.buttonContainer}>
<Button
size={BtnSizes.FULL}
text={
canClaimRewards
? t('superchargeClaimButton')
: userIsVerified
? t('cashIn', { currency: tokenConfigToSupercharge.tokenSymbol })
: t('connectNumber')
}
icon={canClaimRewards && <Logo height={24} type={LogoTypes.LIGHT} />}
showLoading={showLoadingIndicator || claimRewardsLoading}
disabled={showLoadingIndicator || claimRewardsLoading}
onPress={onPressCTA}
testID="ConsumerIncentives/CTA"
/>
</View>

{/* If the restricted supercharge promo rule applies, prevent the user from seeing cash in CTA */}
{restrictSuperchargeForClaimOnly && userIsVerified && !canClaimRewards ? null : (
<View style={styles.buttonContainer}>
<Button
size={BtnSizes.FULL}
text={
canClaimRewards
? t('superchargeClaimButton')
: userIsVerified
? t('cashIn', { currency: tokenConfigToSupercharge.tokenSymbol })
: t('connectNumber')
}
icon={canClaimRewards && <Logo height={24} type={LogoTypes.LIGHT} />}
showLoading={showLoadingIndicator || claimRewardsLoading}
disabled={showLoadingIndicator || claimRewardsLoading}
onPress={onPressCTA}
testID="ConsumerIncentives/CTA"
/>
</View>
)}
</SafeAreaView>
)
}
Expand Down
8 changes: 7 additions & 1 deletion src/home/NotificationBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {
getOutgoingPaymentRequests,
} from 'src/paymentRequest/selectors'
import useSelector from 'src/redux/useSelector'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import variables from 'src/styles/variables'
import { getContentForCurrentLang } from 'src/utils/contentTranslations'
import Logger from 'src/utils/Logger'
Expand Down Expand Up @@ -93,6 +95,10 @@ export function useSimpleActions() {

const dispatch = useDispatch()

const restrictSuperchargeForClaimOnly = getFeatureGate(
StatsigFeatureGates.RESTRICT_SUPERCHARGE_FOR_CLAIM_ONLY
)

useEffect(() => {
dispatch(fetchAvailableRewards())
}, [])
Expand Down Expand Up @@ -224,7 +230,7 @@ export function useSimpleActions() {
})
}

if (!isSupercharging && !dismissedStartSupercharging) {
if (!isSupercharging && !restrictSuperchargeForClaimOnly && !dismissedStartSupercharging) {
actions.push({
id: NotificationType.start_supercharging,
type: NotificationType.start_supercharging,
Expand Down
26 changes: 26 additions & 0 deletions src/home/NotificationCenter.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { NotificationBannerCTATypes, NotificationType } from 'src/home/types'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { cancelPaymentRequest, updatePaymentRequestNotified } from 'src/paymentRequest/actions'
import { getFeatureGate } from 'src/statsig'
import { Spacing } from 'src/styles/styles'
import { multiplyByWei } from 'src/utils/formatting'
import { createMockStore, getElementText, getMockStackScreenProps } from 'test/utils'
Expand Down Expand Up @@ -40,6 +41,7 @@ jest.mock('src/navigator/NavigationService', () => ({
ensurePincode: jest.fn(async () => true),
navigate: jest.fn(),
}))
jest.mock('src/statsig')

const DEVICE_HEIGHT = 850

Expand Down Expand Up @@ -991,6 +993,30 @@ describe('NotificationCenter', () => {
expect(queryByTestId('NotificationView/start_supercharging')).toBeFalsy()
})

it('does not render start supercharging because the user is in a restricted region', () => {
jest.mocked(getFeatureGate).mockReturnValueOnce(true)

const store = createMockStore({
...superchargeWithoutRewardsSetUp,
account: {
...superchargeWithoutRewardsSetUp.account,
dismissedStartSupercharging: false,
},
tokens: {
tokenBalances: mockcUsdWithoutEnoughBalance,
},
})
const { queryByTestId } = render(
<Provider store={store}>
<NotificationCenter {...getMockStackScreenProps(Screens.NotificationCenter)} />
</Provider>
)

expect(queryByTestId('NotificationView/supercharge_available')).toBeFalsy()
expect(queryByTestId('NotificationView/supercharging')).toBeFalsy()
expect(queryByTestId('NotificationView/start_supercharging')).toBeFalsy()
})

it('emits correct analytics event when CTA button is pressed', () => {
const store = createMockStore({
...superchargeWithoutRewardsSetUp,
Expand Down
67 changes: 66 additions & 1 deletion src/navigator/RewardsPill.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,37 @@ import { Provider } from 'react-redux'
import { navigate } from 'src/navigator/NavigationService'
import RewardsPill from 'src/navigator/RewardsPill'
import { Screens } from 'src/navigator/Screens'
import { getFeatureGate } from 'src/statsig'
import { createMockStore } from 'test/utils'
import { mockAccount } from 'test/values'
import { mockAccount, mockCusdAddress, mockCusdTokenId, mockTokenBalances } from 'test/values'

jest.mock('src/statsig')

const stateWithInsufficientBalanceForSupercharge = {
app: {
phoneNumberVerified: true,
superchargeTokenConfigByToken: {
[mockCusdAddress]: {
minBalance: 10,
maxBalance: 1000,
},
},
},
tokens: {
tokenBalances: {
[mockCusdTokenId]: mockTokenBalances[mockCusdTokenId],
},
},
}

const stateWithSuperchargeEnabled = {
...stateWithInsufficientBalanceForSupercharge,
tokens: {
tokenBalances: {
[mockCusdTokenId]: { ...mockTokenBalances[mockCusdTokenId], balance: '50' },
},
},
}

describe('RewardsPill', () => {
it('renders correctly', () => {
Expand Down Expand Up @@ -34,4 +63,40 @@ describe('RewardsPill', () => {
fireEvent.press(getByTestId('EarnRewards'))
expect(navigate).toBeCalledWith(Screens.ConsumerIncentivesHomeScreen)
})

it('renders if the user is eligible for rewards in a restricted environment', () => {
jest.mocked(getFeatureGate).mockReturnValue(true)

const { getByTestId } = render(
<Provider store={createMockStore(stateWithSuperchargeEnabled)}>
<RewardsPill />
</Provider>
)

expect(getByTestId('EarnRewards')).toBeTruthy()
})

it('does not render if the user is not eligible for rewards in a restricted environment', () => {
jest.mocked(getFeatureGate).mockReturnValue(true)

const { queryByTestId } = render(
<Provider store={createMockStore(stateWithInsufficientBalanceForSupercharge)}>
<RewardsPill />
</Provider>
)

expect(queryByTestId('EarnRewards')).toBeFalsy()
})

it('renders if the user is not eligible for rewards in a non-restricted environment', () => {
jest.mocked(getFeatureGate).mockReturnValue(false)

const { getByTestId } = render(
<Provider store={createMockStore(stateWithInsufficientBalanceForSupercharge)}>
<RewardsPill />
</Provider>
)

expect(getByTestId('EarnRewards')).toBeTruthy()
})
})
19 changes: 15 additions & 4 deletions src/navigator/RewardsPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,40 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
import { RewardsEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { rewardsEnabledSelector } from 'src/app/selectors'
import { phoneNumberVerifiedSelector, rewardsEnabledSelector } from 'src/app/selectors'
import Pill from 'src/components/Pill'
import { isE2EEnv } from 'src/config'
import { RewardsScreenOrigin } from 'src/consumerIncentives/analyticsEventsTracker'
import { superchargeInfoSelector } from 'src/consumerIncentives/selectors'
import Rings from 'src/icons/Rings'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import useSelector from 'src/redux/useSelector'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'

function RewardsPill() {
const { t } = useTranslation()

const rewardsEnabled = useSelector(rewardsEnabledSelector)
const phoneNumberVerified = useSelector(phoneNumberVerifiedSelector)
const { hasBalanceForSupercharge } = useSelector(superchargeInfoSelector)
const restrictSuperchargeForClaimOnly = getFeatureGate(
StatsigFeatureGates.RESTRICT_SUPERCHARGE_FOR_CLAIM_ONLY
)
const isSupercharging = phoneNumberVerified && hasBalanceForSupercharge

const onOpenRewards = () => {
navigate(Screens.ConsumerIncentivesHomeScreen)
ValoraAnalytics.track(RewardsEvents.rewards_screen_opened, {
origin: RewardsScreenOrigin.RewardPill,
})
}

const rewardsEnabled = useSelector(rewardsEnabledSelector)
const showRewardsPill = isE2EEnv || rewardsEnabled
const hideRewardsPill =
(restrictSuperchargeForClaimOnly && !isSupercharging) || (!isE2EEnv && !rewardsEnabled)

if (!showRewardsPill) {
if (hideRewardsPill) {
return null
}
return <Pill text={t('rewards')} icon={<Rings />} onPress={onOpenRewards} testID="EarnRewards" />
Expand Down
1 change: 1 addition & 0 deletions src/statsig/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const FeatureGates = {
[StatsigFeatureGates.SHOW_CLOUD_ACCOUNT_BACKUP_SETUP]: false,
[StatsigFeatureGates.SHOW_CLOUD_ACCOUNT_BACKUP_RESTORE]: false,
[StatsigFeatureGates.USE_VIEM_FOR_SEND]: false,
[StatsigFeatureGates.RESTRICT_SUPERCHARGE_FOR_CLAIM_ONLY]: false,
}

export const ExperimentConfigs = {
Expand Down
1 change: 1 addition & 0 deletions src/statsig/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export enum StatsigFeatureGates {
SHOW_CLOUD_ACCOUNT_BACKUP_SETUP = 'show_cloud_account_backup_setup',
SHOW_CLOUD_ACCOUNT_BACKUP_RESTORE = 'show_cloud_account_backup_restore',
USE_VIEM_FOR_SEND = 'use_viem_for_send',
RESTRICT_SUPERCHARGE_FOR_CLAIM_ONLY = 'restrict_supercharge_for_claim_only',
}

export enum StatsigExperiments {
Expand Down

0 comments on commit 2c4c767

Please sign in to comment.