Skip to content

Commit 8db7694

Browse files
authored
Merge branch 'main' into price-alerts-price-percent
2 parents 2e52bf0 + 3552457 commit 8db7694

20 files changed

Lines changed: 656 additions & 184 deletions

File tree

.github/workflows/create-release-pr.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ jobs:
109109
# - contents: write → push the release / version-bump / changelog branches
110110
# - pull_requests: write → open the release PRs
111111
# - members: read → auto-changelog --autoCategorize resolves PR author teams (read:org)
112+
# - workflows: write → the release branch cut here comes from main and can genuinely
113+
# differ in .github/workflows/* from the previous release branch
112114
# OIDC works on every trigger, so no workflow_dispatch vs workflow_call token split is needed.
113115
- name: Get token
114116
id: get-token
@@ -119,6 +121,7 @@ jobs:
119121
contents: write
120122
pull_requests: write
121123
members: read
124+
workflows: write
122125
123126
- name: Create Release PR (semver / native version bump)
124127
if: needs.resolve-bases.outputs.is_ota != 'true'

.github/workflows/update-release-changelog.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ jobs:
5454
pull-requests: write
5555
id-token: write
5656
steps:
57+
# Mint a short-lived token via the token-exchange service (OIDC). It needs:
58+
# - contents: write → push the changelog branch
59+
# - pull_requests: write → open/update the changelog PR
60+
# - workflows: write → workaround for a GitHub push-protection timeout bug on new
61+
# branch pushes (see actions/checkout#2287); this step never
62+
# actually touches workflow files.
5763
- name: Get token
5864
id: get-token
5965
uses: MetaMask/github-tools/.github/actions/get-token@v1
@@ -62,6 +68,7 @@ jobs:
6268
permissions: |
6369
contents: write
6470
pull_requests: write
71+
workflows: write
6572
6673
- name: Checkout release branch
6774
uses: actions/checkout@v4

app/components/UI/Perps/Perps.testIds.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ export const PerpsMarketDetailsViewSelectorsIDs = {
412412
LOADING: 'perps-market-details-loading',
413413
ERROR: 'perps-market-details-error',
414414
HEADER: 'perps-market-header',
415+
MARKET_SUMMARY: 'perps-market-details-market-summary',
416+
FULLSCREEN_CHART_BUTTON: 'perps-market-details-fullscreen-chart-button',
415417
STATISTICS_HIGH_24H: 'perps-statistics-high-24h',
416418
STATISTICS_LOW_24H: 'perps-statistics-low-24h',
417419
STATISTICS_VOLUME_24H: 'perps-statistics-volume-24h',
@@ -456,7 +458,8 @@ export const PerpsMarketHeaderSelectorsIDs = {
456458
PRICE_CHANGE: 'perps-market-header-price-change',
457459
MORE_BUTTON: 'perps-market-header-more-button',
458460
FAVORITE_BUTTON: 'perps-market-header-favorite-button',
459-
CATEGORY_SEARCH_BUTTON: 'perps-market-header-category-search-button',
461+
MARKET_LIST_BUTTON: 'perps-market-header-market-list-button',
462+
SUBTITLE: 'perps-market-header-subtitle',
460463
};
461464

462465
// ========================================

app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.styles.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ export const createStyles = ({ theme }: { theme: Theme }) =>
2828
paddingBottom: 16, // Consistent bottom padding
2929
},
3030

31+
// Keeps the fullscreen button fixed while the price row shrinks
32+
marketSummaryFullscreenButton: {
33+
flexShrink: 0,
34+
},
35+
3136
// Container for error state
3237
container: {
3338
flex: 1,

app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.test.tsx

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,7 +2143,7 @@ describe('PerpsMarketDetailsView', () => {
21432143
await act(async () => {
21442144
fireEvent.press(
21452145
getByTestId(
2146-
`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`,
2146+
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
21472147
),
21482148
);
21492149
});
@@ -3493,7 +3493,7 @@ describe('PerpsMarketDetailsView', () => {
34933493

34943494
// Press fullscreen button
34953495
const fullscreenButton = getByTestId(
3496-
'perps-market-header-fullscreen-button',
3496+
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
34973497
);
34983498
fireEvent.press(fullscreenButton);
34993499

@@ -3517,7 +3517,7 @@ describe('PerpsMarketDetailsView', () => {
35173517

35183518
// Open the modal first
35193519
const fullscreenButton = getByTestId(
3520-
'perps-market-header-fullscreen-button',
3520+
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
35213521
);
35223522
fireEvent.press(fullscreenButton);
35233523

@@ -3550,7 +3550,7 @@ describe('PerpsMarketDetailsView', () => {
35503550

35513551
// Open the modal
35523552
const fullscreenButton = getByTestId(
3553-
'perps-market-header-fullscreen-button',
3553+
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
35543554
);
35553555
fireEvent.press(fullscreenButton);
35563556

@@ -3615,9 +3615,7 @@ describe('PerpsMarketDetailsView', () => {
36153615
);
36163616

36173617
fireEvent.press(
3618-
getByTestId(
3619-
`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`,
3620-
),
3618+
getByTestId(PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON),
36213619
);
36223620

36233621
await waitFor(() => {
@@ -3640,8 +3638,8 @@ describe('PerpsMarketDetailsView', () => {
36403638
});
36413639
});
36423640

3643-
describe('Category search shortcut', () => {
3644-
it('navigates to market list without filters when search button is pressed', () => {
3641+
describe('Market list shortcut', () => {
3642+
it('navigates to market list without filters when the market list button is pressed', () => {
36453643
const { getByTestId } = renderWithProvider(
36463644
<PerpsConnectionProvider>
36473645
<PerpsMarketDetailsView />
@@ -3651,15 +3649,50 @@ describe('PerpsMarketDetailsView', () => {
36513649
},
36523650
);
36533651

3654-
const searchButton = getByTestId(
3655-
'perps-market-header-category-search-button',
3652+
const marketListButton = getByTestId(
3653+
PerpsMarketHeaderSelectorsIDs.MARKET_LIST_BUTTON,
36563654
);
3657-
fireEvent.press(searchButton);
3655+
fireEvent.press(marketListButton);
36583656

36593657
expect(mockNavigateToMarketList).toHaveBeenCalledWith({
3660-
source: 'magnifying_glass',
3658+
source: 'perp_asset_screen',
36613659
});
36623660
});
3661+
3662+
it('tracks the market list button click with the correct analytics values', () => {
3663+
const mockTrack = jest.fn();
3664+
const { usePerpsEventTracking: mockUsePerpsEventTrackingFn } =
3665+
jest.requireMock('../../hooks/usePerpsEventTracking');
3666+
mockUsePerpsEventTrackingFn.mockImplementation(() => ({
3667+
track: mockTrack,
3668+
}));
3669+
3670+
const { getByTestId } = renderWithProvider(
3671+
<PerpsConnectionProvider>
3672+
<PerpsMarketDetailsView />
3673+
</PerpsConnectionProvider>,
3674+
{
3675+
state: initialState,
3676+
},
3677+
);
3678+
3679+
fireEvent.press(
3680+
getByTestId(PerpsMarketHeaderSelectorsIDs.MARKET_LIST_BUTTON),
3681+
);
3682+
3683+
expect(mockTrack).toHaveBeenCalledWith(
3684+
MetaMetricsEvents.PERPS_UI_INTERACTION,
3685+
expect.objectContaining({
3686+
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
3687+
PERPS_EVENT_VALUE.INTERACTION_TYPE.BUTTON_CLICKED,
3688+
[PERPS_EVENT_PROPERTY.BUTTON_CLICKED]:
3689+
PERPS_EVENT_VALUE.BUTTON_CLICKED.MARKET_LIST,
3690+
[PERPS_EVENT_PROPERTY.BUTTON_LOCATION]:
3691+
PERPS_EVENT_VALUE.BUTTON_LOCATION.PERP_MARKET_DETAILS,
3692+
[PERPS_EVENT_PROPERTY.ASSET]: 'BTC',
3693+
}),
3694+
);
3695+
});
36633696
});
36643697

36653698
describe('Position opened timestamp', () => {
@@ -4613,7 +4646,9 @@ describe('PerpsMarketDetailsView', () => {
46134646

46144647
// Should show the route market's leverage badge
46154648
expect(getByText('25x')).toBeOnTheScreen();
4616-
expect(getAllByText('ETH-USD').length).toBeGreaterThanOrEqual(1);
4649+
// Header shows the full asset name and the market pair subtitle
4650+
expect(getByText('Ethereum')).toBeOnTheScreen();
4651+
expect(getAllByText('ETH-USD perp').length).toBeGreaterThanOrEqual(1);
46174652
});
46184653

46194654
it('enriches market data when route maxLeverage is unformatted', async () => {
@@ -4820,7 +4855,8 @@ describe('PerpsMarketDetailsView', () => {
48204855

48214856
// Verify the header renders with correct market symbol
48224857
expect(getByTestId('perps-market-header')).toBeOnTheScreen();
4823-
expect(getAllByText('BTC-USD').length).toBeGreaterThanOrEqual(1);
4858+
expect(getByText('Bitcoin')).toBeOnTheScreen();
4859+
expect(getAllByText('BTC-USD perp').length).toBeGreaterThanOrEqual(1);
48244860

48254861
// Should show the enriched market's leverage badge from usePerpsMarkets
48264862
await waitFor(() => {
@@ -4844,7 +4880,7 @@ describe('PerpsMarketDetailsView', () => {
48444880
isRefreshing: false,
48454881
});
48464882

4847-
const { getAllByText, queryByText } = renderWithProvider(
4883+
const { getByText, getAllByText, queryByText } = renderWithProvider(
48484884
<PerpsConnectionProvider>
48494885
<PerpsMarketDetailsView />
48504886
</PerpsConnectionProvider>,
@@ -4854,7 +4890,8 @@ describe('PerpsMarketDetailsView', () => {
48544890
);
48554891

48564892
// Should show the asset name but no leverage badge (since no maxLeverage available)
4857-
expect(getAllByText('UNKNOWN-USD').length).toBeGreaterThanOrEqual(1);
4893+
expect(getByText('Unknown Asset')).toBeOnTheScreen();
4894+
expect(getAllByText('UNKNOWN-USD perp').length).toBeGreaterThanOrEqual(1);
48584895
// No leverage badge should be shown
48594896
expect(queryByText('40x')).toBeNull();
48604897
expect(queryByText('25x')).toBeNull();

app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.tsx

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import {
22
Box,
3+
BoxAlignItems,
4+
BoxFlexDirection,
5+
BoxJustifyContent,
36
Button as DSButton,
7+
ButtonIcon,
8+
ButtonIconSize,
49
ButtonSemantic,
510
ButtonSemanticSeverity,
611
ButtonVariant,
712
ButtonSize as ButtonSizeRNDesignSystem,
13+
IconName,
814
Text,
915
TextColor,
1016
TextVariant,
17+
useHeaderStandardAnimated,
1118
} from '@metamask/design-system-react-native';
1219
import {
1320
useFocusEffect,
@@ -40,7 +47,6 @@ import { useDispatch, useSelector } from 'react-redux';
4047
import { strings } from '../../../../../../locales/i18n';
4148
import { setPerpsChartPreferredCandlePeriod } from '../../../../../actions/settings';
4249
import { Skeleton } from '../../../../../component-library/components-temp/Skeleton';
43-
import useHeaderStandardAnimated from '../../../../../component-library/components-temp/HeaderStandardAnimated/useHeaderStandardAnimated';
4450
import { useStyles } from '../../../../../component-library/hooks';
4551
import Routes from '../../../../../constants/navigation/Routes';
4652
import Engine from '../../../../../core/Engine';
@@ -58,10 +64,12 @@ import PerpsCompactOrderRow from '../../components/PerpsCompactOrderRow';
5864
import PerpsFlipPositionConfirmSheet from '../../components/PerpsFlipPositionConfirmSheet';
5965
import {
6066
PerpsMarketDetailsViewSelectorsIDs,
67+
PerpsMarketHeaderSelectorsIDs,
6168
PerpsOrderViewSelectorsIDs,
6269
PerpsTutorialSelectorsIDs,
6370
PerpsCompactOrderRowSelectorsIDs,
6471
} from '../../Perps.testIds';
72+
import LivePriceHeader from '../../components/LivePriceDisplay/LivePriceHeader';
6573
import PerpsMarketInlineHeader from '../../components/PerpsMarketInlineHeader';
6674
import PerpsMarketHoursBanner from '../../components/PerpsMarketHoursBanner';
6775
import PerpsMarketStatisticsCard from '../../components/PerpsMarketStatisticsCard';
@@ -853,21 +861,21 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
853861
});
854862
}, [market, isWatchlist, track, showToast, PerpsToastOptions]);
855863

856-
const handleCategorySearchPress = useCallback(() => {
864+
const handleMarketListPress = useCallback(() => {
857865
if (!market) return;
858866

859867
track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
860868
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
861869
PERPS_EVENT_VALUE.INTERACTION_TYPE.BUTTON_CLICKED,
862870
[PERPS_EVENT_PROPERTY.BUTTON_CLICKED]:
863-
PERPS_EVENT_VALUE.BUTTON_CLICKED.MAGNIFYING_GLASS,
871+
PERPS_EVENT_VALUE.BUTTON_CLICKED.MARKET_LIST,
864872
[PERPS_EVENT_PROPERTY.BUTTON_LOCATION]:
865-
PERPS_EVENT_VALUE.BUTTON_LOCATION.ASSET_DETAILS,
873+
PERPS_EVENT_VALUE.BUTTON_LOCATION.PERP_MARKET_DETAILS,
866874
[PERPS_EVENT_PROPERTY.ASSET]: market.symbol,
867875
});
868876

869877
navigateToMarketList({
870-
source: PERPS_EVENT_VALUE.SOURCE.MAGNIFYING_GLASS,
878+
source: PERPS_EVENT_VALUE.SOURCE.PERP_ASSET_SCREEN,
871879
});
872880
}, [market, track, navigateToMarketList]);
873881

@@ -1540,14 +1548,42 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
15401548
market={market}
15411549
currentPrice={syncedChartCurrentPrice}
15421550
onBackPress={handleBackPress}
1543-
onFullscreenPress={handleFullscreenChartOpen}
15441551
onFavoritePress={handleWatchlistPress}
1545-
onCategorySearchPress={handleCategorySearchPress}
1552+
onMarketListPress={handleMarketListPress}
15461553
isFavorite={isWatchlist}
1554+
useDetailLayout
15471555
testID={PerpsMarketDetailsViewSelectorsIDs.HEADER}
1548-
fullscreenButtonTestID={`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`}
15491556
/>
15501557

1558+
{/* Below header: live price + 24h change and fullscreen chart button */}
1559+
<Box
1560+
flexDirection={BoxFlexDirection.Row}
1561+
alignItems={BoxAlignItems.Center}
1562+
justifyContent={BoxJustifyContent.Between}
1563+
gap={2}
1564+
twClassName="px-4 pb-2"
1565+
testID={PerpsMarketDetailsViewSelectorsIDs.MARKET_SUMMARY}
1566+
>
1567+
{/* Flexible wrapper lets the price shrink; the button stays fixed. */}
1568+
<Box twClassName="flex-1">
1569+
<LivePriceHeader
1570+
symbol={market.symbol}
1571+
testIDPrice={PerpsMarketHeaderSelectorsIDs.PRICE}
1572+
testIDChange={PerpsMarketHeaderSelectorsIDs.PRICE_CHANGE}
1573+
currentPrice={syncedChartCurrentPrice}
1574+
size="large"
1575+
/>
1576+
</Box>
1577+
<ButtonIcon
1578+
iconName={IconName.Expand}
1579+
size={ButtonIconSize.Md}
1580+
onPress={handleFullscreenChartOpen}
1581+
style={styles.marketSummaryFullscreenButton}
1582+
testID={PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON}
1583+
accessibilityLabel={strings('perps.market_details.fullscreen_chart')}
1584+
/>
1585+
</Box>
1586+
15511587
<View style={styles.scrollableContentContainer}>
15521588
<ScrollView
15531589
ref={scrollViewRef}

0 commit comments

Comments
 (0)