Skip to content

Commit 44f1c83

Browse files
committed
chore: add metrics segment event
1 parent 1b9ae1b commit 44f1c83

6 files changed

Lines changed: 162 additions & 16 deletions

File tree

app/components/UI/WhatsHappening/components/WhatsHappeningAssetPill.test.tsx

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ import renderWithProvider from '../../../../util/test/renderWithProvider';
44
import WhatsHappeningAssetPill from './WhatsHappeningAssetPill';
55

66
const mockHandleTrade = jest.fn();
7+
const mockTrackEvent = jest.fn();
8+
const mockCreateEventBuilder = jest.fn((eventName: string) => ({
9+
addProperties: jest.fn(() => ({
10+
build: jest.fn(() => ({ category: eventName })),
11+
})),
12+
build: jest.fn(() => ({ category: eventName })),
13+
}));
14+
15+
jest.mock('../../../hooks/useAnalytics/useAnalytics', () => ({
16+
useAnalytics: () => ({
17+
trackEvent: mockTrackEvent,
18+
createEventBuilder: mockCreateEventBuilder,
19+
}),
20+
}));
721

822
jest.mock(
923
'../../../Views/WhatsHappeningDetailView/hooks/useTradeNavigation',
@@ -40,6 +54,23 @@ const baseAsset = {
4054
hlPerpsMarket: ['BTC'] as string[],
4155
};
4256

57+
const baseItem = {
58+
id: 'trend-0',
59+
title: 'Test headline',
60+
description: 'Test description',
61+
date: '2026-01-01T00:00:00.000Z',
62+
impact: 'positive' as const,
63+
relatedAssets: [baseAsset],
64+
articles: [],
65+
};
66+
67+
const defaultProps = {
68+
asset: baseAsset,
69+
item: baseItem,
70+
cardIndex: 0,
71+
source: 'homepage' as const,
72+
};
73+
4374
describe('WhatsHappeningAssetPill', () => {
4475
beforeEach(() => {
4576
jest.clearAllMocks();
@@ -53,16 +84,32 @@ describe('WhatsHappeningAssetPill', () => {
5384
});
5485

5586
it('renders display symbol', () => {
56-
renderWithProvider(<WhatsHappeningAssetPill asset={baseAsset} />);
87+
renderWithProvider(<WhatsHappeningAssetPill {...defaultProps} />);
5788
expect(screen.getByText('BTC')).toBeOnTheScreen();
5889
});
5990

6091
it('calls handleTrade when canTrade and pressed', () => {
61-
renderWithProvider(<WhatsHappeningAssetPill asset={baseAsset} />);
92+
renderWithProvider(<WhatsHappeningAssetPill {...defaultProps} />);
6293
fireEvent.press(screen.getByLabelText('BTC'));
6394
expect(mockHandleTrade).toHaveBeenCalledTimes(1);
6495
});
6596

97+
it('fires WHATS_HAPPENING_INTERACTED with related_asset_pressed when pressed', () => {
98+
renderWithProvider(<WhatsHappeningAssetPill {...defaultProps} />);
99+
fireEvent.press(screen.getByLabelText('BTC'));
100+
expect(mockTrackEvent).toHaveBeenCalledTimes(1);
101+
const builderCall = mockCreateEventBuilder.mock.results[0].value;
102+
expect(builderCall.addProperties).toHaveBeenCalledWith(
103+
expect.objectContaining({
104+
interaction_type: 'related_asset_pressed',
105+
view: 'carousel',
106+
asset_symbol: 'BTC',
107+
perps_market: 'BTC',
108+
asset_caip19: 'eip155:1/slip44:0',
109+
}),
110+
);
111+
});
112+
66113
it('does not wrap in button when canTrade is false', () => {
67114
const useTradeNavigation = jest.requireMock(
68115
'../../../Views/WhatsHappeningDetailView/hooks/useTradeNavigation',
@@ -71,15 +118,15 @@ describe('WhatsHappeningAssetPill', () => {
71118
handleTrade: mockHandleTrade,
72119
canTrade: false,
73120
});
74-
renderWithProvider(<WhatsHappeningAssetPill asset={baseAsset} />);
121+
renderWithProvider(<WhatsHappeningAssetPill {...defaultProps} />);
75122
expect(screen.queryByLabelText('BTC')).toBeNull();
76123
expect(screen.getByText('BTC')).toBeOnTheScreen();
77124
});
78125

79126
it('shows positive percent change when perpsPriceEntry has a positive value', () => {
80127
renderWithProvider(
81128
<WhatsHappeningAssetPill
82-
asset={baseAsset}
129+
{...defaultProps}
83130
perpsPriceEntry={{ price: 95000, percentChange24h: 1.23 }}
84131
/>,
85132
);
@@ -89,22 +136,22 @@ describe('WhatsHappeningAssetPill', () => {
89136
it('shows negative percent change when perpsPriceEntry has a negative value', () => {
90137
renderWithProvider(
91138
<WhatsHappeningAssetPill
92-
asset={baseAsset}
139+
{...defaultProps}
93140
perpsPriceEntry={{ price: 95000, percentChange24h: -2.5 }}
94141
/>,
95142
);
96143
expect(screen.getByText('-2.50%')).toBeOnTheScreen();
97144
});
98145

99146
it('does not render change text when perpsPriceEntry is undefined', () => {
100-
renderWithProvider(<WhatsHappeningAssetPill asset={baseAsset} />);
147+
renderWithProvider(<WhatsHappeningAssetPill {...defaultProps} />);
101148
expect(screen.queryByText(/%/)).toBeNull();
102149
});
103150

104151
it('does not render change text when percentChange24h is undefined', () => {
105152
renderWithProvider(
106153
<WhatsHappeningAssetPill
107-
asset={baseAsset}
154+
{...defaultProps}
108155
perpsPriceEntry={{ price: undefined, percentChange24h: undefined }}
109156
/>,
110157
);

app/components/UI/WhatsHappening/components/WhatsHappeningAssetPill.tsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { memo, useMemo } from 'react';
1+
import React, { memo, useCallback, useMemo } from 'react';
22
import { Pressable } from 'react-native';
33
import { useTailwind } from '@metamask/design-system-twrnc-preset';
44
import type { RelatedAsset } from '@metamask/ai-controllers';
@@ -18,20 +18,36 @@ import { getRelatedAssetImageSource } from '../../../Views/WhatsHappeningDetailV
1818
import useTradeNavigation from '../../../Views/WhatsHappeningDetailView/hooks/useTradeNavigation';
1919
import { formatPercentageChange } from '../../../Views/WhatsHappeningDetailView/utils/formatAssetPrice';
2020
import type { PerpsPriceEntry } from '../../../Views/WhatsHappeningDetailView/hooks/useWhatsHappeningAssetPrices';
21+
import { MetaMetricsEvents } from '../../../../core/Analytics';
22+
import { useAnalytics } from '../../../hooks/useAnalytics/useAnalytics';
23+
import {
24+
WhatsHappeningInteractionType,
25+
WhatsHappeningView,
26+
type WhatsHappeningSourceValue,
27+
} from '../constants';
28+
import { getWhatsHappeningEventProps } from '../eventProperties';
29+
import type { WhatsHappeningItem } from '../types';
2130

2231
const AVATAR_SIZE = 16;
2332

2433
export interface WhatsHappeningAssetPillProps {
2534
asset: RelatedAsset;
2635
perpsPriceEntry?: PerpsPriceEntry;
36+
item: WhatsHappeningItem;
37+
cardIndex: number;
38+
source: WhatsHappeningSourceValue;
2739
}
2840

2941
const WhatsHappeningAssetPill: React.FC<WhatsHappeningAssetPillProps> = ({
3042
asset,
3143
perpsPriceEntry,
44+
item,
45+
cardIndex,
46+
source,
3247
}) => {
3348
const tw = useTailwind();
3449
const { handleTrade, canTrade } = useTradeNavigation(asset);
50+
const { trackEvent, createEventBuilder } = useAnalytics();
3551
const image = useMemo(() => getRelatedAssetImageSource(asset), [asset]);
3652
const displaySymbol = useMemo(
3753
() => getPerpsDisplaySymbol(asset.symbol),
@@ -42,6 +58,32 @@ const WhatsHappeningAssetPill: React.FC<WhatsHappeningAssetPillProps> = ({
4258
[perpsPriceEntry?.percentChange24h],
4359
);
4460

61+
const handlePressWithTracking = useCallback(() => {
62+
trackEvent(
63+
createEventBuilder(MetaMetricsEvents.WHATS_HAPPENING_INTERACTED)
64+
.addProperties({
65+
...getWhatsHappeningEventProps(item, cardIndex, source),
66+
interaction_type: WhatsHappeningInteractionType.RelatedAssetPressed,
67+
view: WhatsHappeningView.Carousel,
68+
asset_symbol: asset.symbol,
69+
perps_market: asset.hlPerpsMarket?.[0],
70+
asset_caip19: asset.caip19?.[0],
71+
})
72+
.build(),
73+
);
74+
handleTrade();
75+
}, [
76+
trackEvent,
77+
createEventBuilder,
78+
item,
79+
cardIndex,
80+
source,
81+
asset.symbol,
82+
asset.hlPerpsMarket,
83+
asset.caip19,
84+
handleTrade,
85+
]);
86+
4587
const inner = (
4688
<Box
4789
flexDirection={BoxFlexDirection.Row}
@@ -77,7 +119,7 @@ const WhatsHappeningAssetPill: React.FC<WhatsHappeningAssetPillProps> = ({
77119
if (canTrade) {
78120
return (
79121
<Pressable
80-
onPress={handleTrade}
122+
onPress={handlePressWithTracking}
81123
accessibilityRole="button"
82124
accessibilityLabel={displaySymbol}
83125
style={({ pressed }) => tw.style('shrink', pressed && 'opacity-80')}

app/components/UI/WhatsHappening/components/WhatsHappeningAssetSlider.test.tsx

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import { screen } from '@testing-library/react-native';
33
import renderWithProvider from '../../../../util/test/renderWithProvider';
44
import WhatsHappeningAssetSlider from './WhatsHappeningAssetSlider';
55

6+
jest.mock('../../../hooks/useAnalytics/useAnalytics', () => ({
7+
useAnalytics: () => ({
8+
trackEvent: jest.fn(),
9+
createEventBuilder: jest.fn(() => ({
10+
addProperties: jest.fn(() => ({ build: jest.fn(() => ({})) })),
11+
build: jest.fn(() => ({})),
12+
})),
13+
}),
14+
}));
15+
616
const mockUseWhatsHappeningAssetPrices = jest.fn();
717

818
jest.mock(
@@ -63,6 +73,22 @@ const noPerpsAsset = {
6373
caip19: ['eip155:1/erc20:0xfoo'],
6474
};
6575

76+
const baseItem = {
77+
id: 'trend-0',
78+
title: 'Test headline',
79+
description: 'Test description',
80+
date: '2026-01-01T00:00:00.000Z',
81+
impact: 'positive' as const,
82+
relatedAssets: [btcAsset, ethAsset],
83+
articles: [],
84+
};
85+
86+
const sharedProps = {
87+
item: baseItem,
88+
cardIndex: 0,
89+
source: 'homepage' as const,
90+
};
91+
6692
describe('WhatsHappeningAssetSlider', () => {
6793
beforeEach(() => {
6894
jest.clearAllMocks();
@@ -73,30 +99,39 @@ describe('WhatsHappeningAssetSlider', () => {
7399

74100
it('renders a pill per perps asset', () => {
75101
renderWithProvider(
76-
<WhatsHappeningAssetSlider assets={[btcAsset, ethAsset]} />,
102+
<WhatsHappeningAssetSlider
103+
{...sharedProps}
104+
assets={[btcAsset, ethAsset]}
105+
/>,
77106
);
78107
expect(screen.getByText('BTC')).toBeOnTheScreen();
79108
expect(screen.getByText('ETH')).toBeOnTheScreen();
80109
});
81110

82111
it('filters out non-perps assets', () => {
83112
renderWithProvider(
84-
<WhatsHappeningAssetSlider assets={[btcAsset, noPerpsAsset]} />,
113+
<WhatsHappeningAssetSlider
114+
{...sharedProps}
115+
assets={[btcAsset, noPerpsAsset]}
116+
/>,
85117
);
86118
expect(screen.getByText('BTC')).toBeOnTheScreen();
87119
expect(screen.queryByText('FOO')).toBeNull();
88120
});
89121

90122
it('calls useWhatsHappeningAssetPrices only with perps assets', () => {
91123
renderWithProvider(
92-
<WhatsHappeningAssetSlider assets={[btcAsset, noPerpsAsset]} />,
124+
<WhatsHappeningAssetSlider
125+
{...sharedProps}
126+
assets={[btcAsset, noPerpsAsset]}
127+
/>,
93128
);
94129
expect(mockUseWhatsHappeningAssetPrices).toHaveBeenCalledWith([btcAsset]);
95130
});
96131

97132
it('returns null when all assets are non-perps', () => {
98133
const { toJSON } = renderWithProvider(
99-
<WhatsHappeningAssetSlider assets={[noPerpsAsset]} />,
134+
<WhatsHappeningAssetSlider {...sharedProps} assets={[noPerpsAsset]} />,
100135
);
101136
expect(toJSON()).toBeNull();
102137
});
@@ -107,7 +142,9 @@ describe('WhatsHappeningAssetSlider', () => {
107142
BTC: { price: 95000, percentChange24h: 1.23 },
108143
},
109144
});
110-
renderWithProvider(<WhatsHappeningAssetSlider assets={[btcAsset]} />);
145+
renderWithProvider(
146+
<WhatsHappeningAssetSlider {...sharedProps} assets={[btcAsset]} />,
147+
);
111148
expect(screen.getByText('+1.23%')).toBeOnTheScreen();
112149
});
113150

@@ -119,7 +156,10 @@ describe('WhatsHappeningAssetSlider', () => {
119156
},
120157
});
121158
renderWithProvider(
122-
<WhatsHappeningAssetSlider assets={[btcAsset, ethAsset]} />,
159+
<WhatsHappeningAssetSlider
160+
{...sharedProps}
161+
assets={[btcAsset, ethAsset]}
162+
/>,
123163
);
124164
expect(screen.getByText('-2.50%')).toBeOnTheScreen();
125165
expect(screen.queryByText('undefined')).toBeNull();

app/components/UI/WhatsHappening/components/WhatsHappeningAssetSlider.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@ import { ScrollView } from 'react-native';
33
import { useTailwind } from '@metamask/design-system-twrnc-preset';
44
import type { RelatedAsset } from '@metamask/ai-controllers';
55
import { useWhatsHappeningAssetPrices } from '../../../Views/WhatsHappeningDetailView/hooks/useWhatsHappeningAssetPrices';
6+
import type { WhatsHappeningItem } from '../types';
7+
import type { WhatsHappeningSourceValue } from '../constants';
68
import WhatsHappeningAssetPill from './WhatsHappeningAssetPill';
79

810
export interface WhatsHappeningAssetSliderProps {
911
assets: RelatedAsset[];
12+
item: WhatsHappeningItem;
13+
cardIndex: number;
14+
source: WhatsHappeningSourceValue;
1015
}
1116

1217
const WhatsHappeningAssetSlider: React.FC<WhatsHappeningAssetSliderProps> = ({
1318
assets,
19+
item,
20+
cardIndex,
21+
source,
1422
}) => {
1523
const tw = useTailwind();
1624

@@ -37,6 +45,9 @@ const WhatsHappeningAssetSlider: React.FC<WhatsHappeningAssetSliderProps> = ({
3745
key={asset.sourceAssetId}
3846
asset={asset}
3947
perpsPriceEntry={perpsPriceBySymbol[asset.hlPerpsMarket?.[0] ?? '']}
48+
item={item}
49+
cardIndex={cardIndex}
50+
source={source}
4051
/>
4152
))}
4253
</ScrollView>

app/components/UI/WhatsHappening/components/WhatsHappeningCard.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ const WhatsHappeningCard: React.FC<WhatsHappeningCardProps> = ({
123123
</Box>
124124

125125
{item.relatedAssets.length > 0 ? (
126-
<WhatsHappeningAssetSlider assets={item.relatedAssets} />
126+
<WhatsHappeningAssetSlider
127+
assets={item.relatedAssets}
128+
item={item}
129+
cardIndex={cardIndex}
130+
source={source}
131+
/>
127132
) : null}
128133
</TouchableOpacity>
129134
</View>

app/components/UI/WhatsHappening/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ export const WhatsHappeningInteractionType = {
2020
SourceClick: 'source_click',
2121
TradePressed: 'trade_pressed',
2222
Pan: 'pan',
23+
RelatedAssetPressed: 'related_asset_pressed',
2324
} as const;

0 commit comments

Comments
 (0)