diff --git a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx index 56c3cc749ae..ead9d7016c8 100644 --- a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx +++ b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx @@ -38,7 +38,7 @@ const StakeInputView = () => { handleCurrencySwitch, currencyToggleValue, percentageOptions, - handleAmountPress, + handleQuickAmountPress, handleKeypadChange, calculateEstimatedAnnualRewards, estimatedAnnualRewards, @@ -174,8 +174,26 @@ const StakeInputView = () => { + withMetaMetrics(handleQuickAmountPress, { + event: MetaMetricsEvents.STAKE_INPUT_QUICK_AMOUNT_CLICKED, + properties: { + location: 'StakeInputView', + amount: value, + // onMaxPress is called instead when it's defined and the max is clicked. + is_max: false, + mode: isEth ? 'native' : 'fiat', + }, + })({ value }) + } + onMaxPress={withMetaMetrics(handleMaxButtonPress, { + event: MetaMetricsEvents.STAKE_INPUT_QUICK_AMOUNT_CLICKED, + properties: { + location: 'StakeInputView', + is_max: true, + mode: isEth ? 'native' : 'fiat', + }, + })} /> { const title = strings('stake.unstake_eth'); const navigation = useNavigation(); const { styles, theme } = useStyles(styleSheet, {}); + const { trackEvent, createEventBuilder } = useMetrics(); const { @@ -38,7 +39,7 @@ const UnstakeInputView = () => { handleCurrencySwitch, currencyToggleValue, percentageOptions, - handleAmountPress, + handleQuickAmountPress, handleKeypadChange, stakedBalanceValue, } = useUnstakingInputHandlers(); @@ -111,7 +112,17 @@ const UnstakeInputView = () => { + withMetaMetrics(handleQuickAmountPress, { + event: MetaMetricsEvents.UNSTAKE_INPUT_QUICK_AMOUNT_CLICKED, + properties: { + location: 'UnstakeInputView', + amount: value, + is_max: value === 1, + mode: isEth ? 'native' : 'fiat', + }, + })({ value }) + } /> & { claimableAmount: string; @@ -27,8 +30,8 @@ type StakeBannerProps = Pick & { const ClaimBanner = ({ claimableAmount, style }: StakeBannerProps) => { const { styles } = useStyles(styleSheet, {}); - const { trackEvent, createEventBuilder } = useMetrics(); + const [isSubmittingClaimTransaction, setIsSubmittingClaimTransaction] = useState(false); @@ -44,10 +47,10 @@ const ClaimBanner = ({ claimableAmount, style }: StakeBannerProps) => { trackEvent( createEventBuilder(MetaMetricsEvents.STAKE_CLAIM_BUTTON_CLICKED) - .addProperties({ - location: 'Token Details' - }) - .build() + .addProperties({ + location: 'Token Details', + }) + .build(), ); setIsSubmittingClaimTransaction(true); diff --git a/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx b/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx index 7622fcabce4..e6245f6506a 100644 --- a/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx +++ b/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx @@ -33,13 +33,13 @@ const StakingButtons = ({ }); trackEvent( createEventBuilder(MetaMetricsEvents.STAKE_WITHDRAW_BUTTON_CLICKED) - .addProperties({ - location: 'Token Details', - text: 'Unstake', - token_symbol: 'ETH', - chain_id: chainId, - }) - .build() + .addProperties({ + location: 'Token Details', + text: 'Unstake', + token_symbol: 'ETH', + chain_id: chainId, + }) + .build(), ); }; @@ -47,13 +47,13 @@ const StakingButtons = ({ navigate('StakeScreens', { screen: Routes.STAKING.STAKE }); trackEvent( createEventBuilder(MetaMetricsEvents.STAKE_BUTTON_CLICKED) - .addProperties({ - location: 'Token Details', - text: 'Stake', - token_symbol: 'ETH', - chain_id: chainId, - }) - .build() + .addProperties({ + location: 'Token Details', + text: 'Stake', + token_symbol: 'ETH', + chain_id: chainId, + }) + .build(), ); }; diff --git a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx index 22512573686..a6e2f4efca0 100644 --- a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx +++ b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx @@ -12,7 +12,7 @@ import Button, { import { strings } from '../../../../../../../locales/i18n'; import { useNavigation } from '@react-navigation/native'; import Routes from '../../../../../../constants/navigation/Routes'; -import { useMetrics, MetaMetricsEvents } from '../../../../../hooks/useMetrics'; +import { MetaMetricsEvents, useMetrics } from '../../../../../hooks/useMetrics'; interface StakingCtaProps extends Pick { estimatedRewardRate: string; @@ -20,9 +20,8 @@ interface StakingCtaProps extends Pick { const StakingCta = ({ estimatedRewardRate, style }: StakingCtaProps) => { const { styles } = useStyles(styleSheet, {}); - const { trackEvent, createEventBuilder } = useMetrics(); - const { navigate } = useNavigation(); + const { trackEvent, createEventBuilder } = useMetrics(); const navigateToLearnMoreModal = () => { navigate('StakeModals', { diff --git a/app/components/UI/Stake/hooks/useInputHandler.ts b/app/components/UI/Stake/hooks/useInputHandler.ts index 8a9bae17c98..9a4271f0fef 100644 --- a/app/components/UI/Stake/hooks/useInputHandler.ts +++ b/app/components/UI/Stake/hooks/useInputHandler.ts @@ -13,7 +13,6 @@ import { fromWei, } from '../../../../util/number'; import { strings } from '../../../../../locales/i18n'; -import { useMetrics, MetaMetricsEvents } from '../../../hooks/useMetrics'; interface InputHandlerParams { balance: BN; @@ -28,8 +27,6 @@ const useInputHandler = ({ balance }: InputHandlerParams) => { const currentCurrency = useSelector(selectCurrentCurrency); const conversionRate = useSelector(selectConversionRate) || 1; - const { trackEvent, createEventBuilder } = useMetrics(); - const isNonZeroAmount = useMemo(() => amountWei.gt(new BN(0)), [amountWei]); const isOverMaximum = useMemo( @@ -97,7 +94,7 @@ const useInputHandler = ({ balance }: InputHandlerParams) => { { value: 1, label: strings('stake.max') }, ]; - const handleAmountPress = useCallback( + const handleQuickAmountPress = useCallback( ({ value }: { value: number }) => { const percentage = value * 100; const amountPercentage = balance.mul(new BN(percentage)).div(new BN(100)); @@ -117,18 +114,8 @@ const useInputHandler = ({ balance }: InputHandlerParams) => { 2, ).toString(); setFiatAmount(newFiatAmount); - trackEvent( - createEventBuilder(MetaMetricsEvents.STAKE_INPUT_AMOUNT_CLICKED) - .addProperties({ - location: 'Stake', - amount: value, - is_max: value === 1, - mode: isEth ? 'native' : 'fiat', - }) - .build(), - ); }, - [balance, conversionRate, createEventBuilder, isEth, trackEvent], + [balance, conversionRate], ); const handleMaxInput = useCallback( @@ -149,18 +136,8 @@ const useInputHandler = ({ balance }: InputHandlerParams) => { 2, ).toString(); setFiatAmount(fiatValue); - trackEvent( - createEventBuilder(MetaMetricsEvents.STAKE_INPUT_AMOUNT_CLICKED) - .addProperties({ - location: 'Stake', - amount: ethValue, - is_max: true, - mode: isEth ? 'native' : 'fiat', - }) - .build(), - ); }, - [conversionRate, createEventBuilder, isEth, trackEvent], + [conversionRate], ); const currencyToggleValue = isEth @@ -180,7 +157,7 @@ const useInputHandler = ({ balance }: InputHandlerParams) => { handleKeypadChange, handleCurrencySwitch, percentageOptions, - handleAmountPress, + handleQuickAmountPress, currentCurrency, conversionRate, handleMaxInput, diff --git a/app/components/UI/Stake/hooks/useStakingInput.ts b/app/components/UI/Stake/hooks/useStakingInput.ts index 5920a3f633c..6167437c07f 100644 --- a/app/components/UI/Stake/hooks/useStakingInput.ts +++ b/app/components/UI/Stake/hooks/useStakingInput.ts @@ -20,7 +20,7 @@ const useStakingInputHandlers = () => { handleKeypadChange, handleCurrencySwitch, percentageOptions, - handleAmountPress, + handleQuickAmountPress, currentCurrency, handleEthInput, handleFiatInput, @@ -121,7 +121,7 @@ const useStakingInputHandlers = () => { handleKeypadChange, handleCurrencySwitch, percentageOptions, - handleAmountPress, + handleQuickAmountPress, currentCurrency, conversionRate, estimatedAnnualRewards, diff --git a/app/components/UI/Stake/hooks/useUnstakingInput.ts b/app/components/UI/Stake/hooks/useUnstakingInput.ts index 8fb6bc229cf..aecfa6c28d5 100644 --- a/app/components/UI/Stake/hooks/useUnstakingInput.ts +++ b/app/components/UI/Stake/hooks/useUnstakingInput.ts @@ -20,7 +20,7 @@ const useUnstakingInputHandlers = () => { handleKeypadChange, handleCurrencySwitch, percentageOptions, - handleAmountPress, + handleQuickAmountPress, currentCurrency, } = useInputHandler({ balance: new BN(stakedBalanceWei) }); @@ -39,7 +39,7 @@ const useUnstakingInputHandlers = () => { handleCurrencySwitch, currencyToggleValue, percentageOptions, - handleAmountPress, + handleQuickAmountPress, handleKeypadChange, stakedBalanceValue, }; diff --git a/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.test.ts b/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.test.ts new file mode 100644 index 00000000000..355deefef6b --- /dev/null +++ b/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.test.ts @@ -0,0 +1,99 @@ +import { withMetaMetrics } from './withMetaMetrics'; +import { MetaMetrics } from '../../../../../core/Analytics'; +import { MetaMetricsEvents } from '../../../../hooks/useMetrics'; + +describe('withMetaMetrics', () => { + let trackEventSpy: jest.SpyInstance; + + const MOCK_HANDLER_RESULT = 123; + const mockHandler = () => MOCK_HANDLER_RESULT; + const mockAsyncHandler = async () => MOCK_HANDLER_RESULT; + + beforeEach(() => { + jest.resetAllMocks(); + trackEventSpy = jest.spyOn(MetaMetrics.getInstance(), 'trackEvent'); + }); + + it('fires single event when wrapping sync function', () => { + const mockHandlerWithMetaMetrics = withMetaMetrics(mockHandler, { + event: MetaMetricsEvents.STAKE_BUTTON_CLICKED, + properties: { + sample: 'value', + }, + }); + + const result = mockHandlerWithMetaMetrics(); + + expect(result).toEqual(MOCK_HANDLER_RESULT); + expect(trackEventSpy).toHaveBeenCalledTimes(1); + }); + + it('fires array of events when wrapping sync function', () => { + const mockHandlerWithMetaMetrics = withMetaMetrics(mockHandler, [ + { + event: MetaMetricsEvents.TOOLTIP_OPENED, + properties: { + selected_provider: 'consensys', + text: 'Tooltip Opened', + location: 'Unit Test', + tooltip_name: 'Test Tooltip 1', + }, + }, + { + event: MetaMetricsEvents.TOOLTIP_OPENED, + properties: { + selected_provider: 'consensys', + text: 'Tooltip Opened', + location: 'Unit Test', + tooltip_name: 'Test Tooltip 2', + }, + }, + ]); + + const result = mockHandlerWithMetaMetrics(); + expect(result).toEqual(MOCK_HANDLER_RESULT); + expect(trackEventSpy).toHaveBeenCalledTimes(2); + }); + + it('fires single event when wrapping async function', async () => { + const mockAsyncHandlerWithMetaMetrics = withMetaMetrics(mockAsyncHandler, { + event: MetaMetricsEvents.STAKE_BUTTON_CLICKED, + properties: { + sample: 'value', + }, + }); + + const result = await mockAsyncHandlerWithMetaMetrics(); + + expect(result).toEqual(MOCK_HANDLER_RESULT); + expect(trackEventSpy).toHaveBeenCalledTimes(1); + }); + + it('fires all events when wrapping async function', async () => { + const mockAsyncHandlerWithMetaMetrics = withMetaMetrics(mockAsyncHandler, [ + { + event: MetaMetricsEvents.TOOLTIP_OPENED, + properties: { + selected_provider: 'consensys', + text: 'Tooltip Opened', + location: 'Unit Test', + tooltip_name: 'Test Tooltip 1', + }, + }, + { + event: MetaMetricsEvents.TOOLTIP_OPENED, + properties: { + selected_provider: 'consensys', + text: 'Tooltip Opened', + location: 'Unit Test', + tooltip_name: 'Test Tooltip 2', + }, + }, + ]); + + const result = await mockAsyncHandlerWithMetaMetrics(); + + expect(result).toEqual(MOCK_HANDLER_RESULT); + expect(trackEventSpy).toHaveBeenCalledTimes(2); + }); +}); diff --git a/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.ts b/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.ts index ba828b96577..f32f4b5250e 100644 --- a/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.ts +++ b/app/components/UI/Stake/utils/metaMetrics/withMetaMetrics.ts @@ -27,7 +27,8 @@ const buildEvent = (e: WithMetaMetricsEvent) => { return eventBuilder.build(); }; -export const withMetaMetrics = unknown>( +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const withMetaMetrics = any>( func: T, events: WithMetaMetricsEvent | WithMetaMetricsEvent[], ) => { diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts index 1925896b1fc..ed8898687ef 100644 --- a/app/core/Analytics/MetaMetrics.events.ts +++ b/app/core/Analytics/MetaMetrics.events.ts @@ -277,7 +277,8 @@ enum EVENT_NAME { STAKE_BUTTON_CLICKED = 'Stake Button Clicked', REVIEW_STAKE_BUTTON_CLICKED = 'Review Stake Button Clicked', REVIEW_UNSTAKE_BUTTON_CLICKED = 'Review Unstake Button Clicked', - STAKE_INPUT_AMOUNT_CLICKED = 'Stake Input Amount Clicked', + STAKE_INPUT_QUICK_AMOUNT_CLICKED = 'Stake Input Quick Amount Clicked', + UNSTAKE_INPUT_QUICK_AMOUNT_CLICKED = 'Unstake Input Quick Amount Clicked', STAKE_WITHDRAW_BUTTON_CLICKED = 'Stake Withdraw Button Clicked', STAKE_CLAIM_BUTTON_CLICKED = 'Stake Claim Button Clicked', STAKE_LEARN_MORE_CLICKED = 'Stake Learn More Clicked', @@ -896,8 +897,11 @@ const events = { REVIEW_UNSTAKE_BUTTON_CLICKED: generateOpt( EVENT_NAME.REVIEW_UNSTAKE_BUTTON_CLICKED, ), - STAKE_INPUT_AMOUNT_CLICKED: generateOpt( - EVENT_NAME.STAKE_INPUT_AMOUNT_CLICKED, + STAKE_INPUT_QUICK_AMOUNT_CLICKED: generateOpt( + EVENT_NAME.STAKE_INPUT_QUICK_AMOUNT_CLICKED, + ), + UNSTAKE_INPUT_QUICK_AMOUNT_CLICKED: generateOpt( + EVENT_NAME.UNSTAKE_INPUT_QUICK_AMOUNT_CLICKED, ), STAKE_WITHDRAW_BUTTON_CLICKED: generateOpt( EVENT_NAME.STAKE_WITHDRAW_BUTTON_CLICKED,