Skip to content

Commit 3695aa8

Browse files
authored
Merge branch 'main' into perps/tat-1984-perps-home-back-arrow
2 parents 8fcdae0 + 77ef41d commit 3695aa8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2485
-1530
lines changed

.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ enableScripts: false
77
nodeLinker: node-modules
88

99
npmAuditIgnoreAdvisories:
10+
- 1109627 # TODO: Upgrade @react-native-community/cli to 17.0.1+ when ready. Suppressing for now to unblock CI.
1011

1112
yarnPath: .yarn/releases/yarn-4.10.3.cjs
1213

app/component-library/components/List/ListItemSelect/ListItemSelect.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,21 @@ describe('ListItemSelect', () => {
174174
const component = getByTestId('list-item-select');
175175
expect(component).toBeOnTheScreen();
176176
});
177+
178+
it('passes through custom listItemProps', () => {
179+
const { getByTestId } = render(
180+
<ListItemSelect
181+
onPress={() => null}
182+
listItemProps={{
183+
accessibilityHint: 'Custom Hint',
184+
testID: 'nested-list-item',
185+
}}
186+
>
187+
<View testID="test-content">Test Content</View>
188+
</ListItemSelect>,
189+
);
190+
191+
const component = getByTestId('nested-list-item');
192+
expect(component.props.accessibilityHint).toBe('Custom Hint');
193+
});
177194
});

app/component-library/components/List/ListItemSelect/ListItemSelect.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const ListItemSelect: React.FC<ListItemSelectProps> = ({
2222
onLongPress,
2323
gap = DEFAULT_SELECTITEM_GAP,
2424
verticalAlignment,
25+
listItemProps,
2526
...props
2627
}) => {
2728
const { styles } = useStyles(styleSheet, { style, isDisabled });
@@ -34,7 +35,7 @@ const ListItemSelect: React.FC<ListItemSelectProps> = ({
3435
onLongPress={onLongPress}
3536
{...props}
3637
>
37-
<ListItem gap={gap} style={styles.listItem}>
38+
<ListItem gap={gap} style={styles.listItem} {...listItemProps}>
3839
{children}
3940
</ListItem>
4041
{isSelected && (

app/component-library/components/List/ListItemSelect/ListItemSelect.types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export interface ListItemSelectProps
1818
* Optional prop to determine if the item is disabled.
1919
*/
2020
isDisabled?: boolean;
21+
/**
22+
* Optional prop to configure the prop of nested ListItem
23+
*/
24+
listItemProps?: Partial<ListItemProps>;
2125
}
2226

2327
/**

app/components/Nav/Main/MainNavigator.js

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect, useMemo } from 'react';
1+
import React, { useState, useEffect, useMemo, useCallback } from 'react';
22
import { Image, StyleSheet, Keyboard, Platform } from 'react-native';
33
import { createStackNavigator } from '@react-navigation/stack';
44
import { useSelector } from 'react-redux';
@@ -271,6 +271,15 @@ const RewardsHome = () => (
271271
</Stack.Navigator>
272272
);
273273

274+
// Persist the last trending screen across unmounts
275+
export const lastTrendingScreenRef = { current: 'TrendingFeed' };
276+
277+
// Callback to update the last trending screen (outside component to persist)
278+
export const updateLastTrendingScreen = (screenName) => {
279+
// eslint-disable-next-line react-compiler/react-compiler
280+
lastTrendingScreenRef.current = screenName;
281+
};
282+
274283
const TrendingHome = () => (
275284
<Stack.Navigator mode="modal" screenOptions={clearStackNavigatorOptions}>
276285
<Stack.Screen
@@ -667,23 +676,26 @@ const HomeTabs = () => {
667676
options={options.home}
668677
component={WalletTabModalFlow}
669678
/>
670-
{isAssetsTrendingTokensEnabled && (
679+
{isAssetsTrendingTokensEnabled ? (
671680
<Tab.Screen
672681
name={Routes.TRENDING_VIEW}
673682
options={options.trending}
674683
component={TrendingHome}
675684
layout={({ children }) => UnmountOnBlurComponent(children)}
676685
/>
686+
) : (
687+
<Tab.Screen
688+
name={Routes.BROWSER.HOME}
689+
options={{
690+
...options.browser,
691+
tabBarButton: isAssetsTrendingTokensEnabled
692+
? () => null
693+
: undefined,
694+
}}
695+
component={BrowserFlow}
696+
layout={({ children }) => <UnmountOnBlur>{children}</UnmountOnBlur>}
697+
/>
677698
)}
678-
<Tab.Screen
679-
name={Routes.BROWSER.HOME}
680-
options={{
681-
...options.browser,
682-
tabBarButton: isAssetsTrendingTokensEnabled ? () => null : undefined,
683-
}}
684-
component={BrowserFlow}
685-
layout={({ children }) => <UnmountOnBlur>{children}</UnmountOnBlur>}
686-
/>
687699
<Tab.Screen
688700
name={Routes.MODAL.TRADE_WALLET_ACTIONS}
689701
options={options.trade}

app/components/UI/Perps/hooks/usePerpsPositionData.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,13 @@ export const usePerpsPositionData = ({
224224
clearInterval(intervalId);
225225
DevLogger.log('Cleared candle refresh interval');
226226
};
227+
// Note: candleData is intentionally excluded from deps to prevent infinite loop
228+
// This effect only needs to re-run when the interval settings change
229+
// eslint-disable-next-line react-hooks/exhaustive-deps
227230
}, [
228-
candleData,
229231
isLoadingHistory,
230232
selectedInterval,
231233
fetchHistoricalCandles,
232-
initializationState,
233234
isControllerInitialized,
234235
]);
235236

app/components/UI/Predict/components/PredictConsentSheet/PredictConsentSheet.test.tsx

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
import React, { useRef, useEffect } from 'react';
22
import { render, fireEvent, act } from '@testing-library/react-native';
3+
import { InteractionManager } from 'react-native';
34

45
// Internal dependencies
56
import PredictConsentSheet, {
67
PredictConsentSheetRef,
78
} from './PredictConsentSheet';
89
import { usePredictAgreement } from '../../hooks/usePredictAgreement';
910

11+
const mockNavigate = jest.fn();
12+
const runAfterInteractionsCallbacks: (() => void)[] = [];
13+
const mockRunAfterInteractions = jest.spyOn(
14+
InteractionManager,
15+
'runAfterInteractions',
16+
);
17+
const runAfterInteractionsMockImpl: typeof InteractionManager.runAfterInteractions =
18+
(task) => {
19+
if (typeof task === 'function') {
20+
runAfterInteractionsCallbacks.push(task as () => void);
21+
}
22+
23+
return {
24+
then: jest.fn(),
25+
done: jest.fn(),
26+
cancel: jest.fn(),
27+
} as ReturnType<typeof InteractionManager.runAfterInteractions>;
28+
};
29+
1030
// Mock dependencies
1131
jest.mock('react-native/Libraries/Linking/Linking', () => ({
1232
openURL: jest.fn(),
@@ -36,7 +56,7 @@ jest.mock('@metamask/design-system-twrnc-preset', () => ({
3656

3757
jest.mock('@react-navigation/native', () => ({
3858
useNavigation: () => ({
39-
navigate: jest.fn(),
59+
navigate: mockNavigate,
4060
goBack: jest.fn(),
4161
}),
4262
}));
@@ -184,14 +204,21 @@ describe('PredictConsentSheet', () => {
184204

185205
beforeEach(() => {
186206
jest.clearAllMocks();
207+
runAfterInteractionsCallbacks.length = 0;
208+
mockRunAfterInteractions.mockImplementation(runAfterInteractionsMockImpl);
187209
(usePredictAgreement as jest.Mock).mockReturnValue({
188210
isAgreementAccepted: false,
189211
acceptAgreement: mockAcceptAgreement,
190212
});
191213
});
192214

193215
afterEach(() => {
194-
jest.resetAllMocks();
216+
jest.clearAllMocks();
217+
mockRunAfterInteractions.mockReset();
218+
});
219+
220+
afterAll(() => {
221+
mockRunAfterInteractions.mockRestore();
195222
});
196223

197224
describe('visibility behavior', () => {
@@ -526,4 +553,79 @@ describe('PredictConsentSheet', () => {
526553
expect(customAcceptAgreement).toHaveBeenCalledTimes(1);
527554
});
528555
});
556+
557+
describe('Terms Link', () => {
558+
it('renders learn more text', () => {
559+
const TestComponent = () => {
560+
const ref = useRef<PredictConsentSheetRef>(null);
561+
562+
useEffect(() => {
563+
act(() => {
564+
ref.current?.onOpenBottomSheet();
565+
});
566+
}, []);
567+
568+
return (
569+
<PredictConsentSheet
570+
ref={ref}
571+
providerId={mockProviderId}
572+
onDismiss={mockOnDismiss}
573+
onAgree={mockOnAgree}
574+
/>
575+
);
576+
};
577+
578+
const { getByText } = render(<TestComponent />);
579+
580+
expect(getByText('Learn more')).toBeOnTheScreen();
581+
});
582+
583+
it('navigates to polymarket terms webview when description is pressed', () => {
584+
const TestComponent = () => {
585+
const ref = useRef<PredictConsentSheetRef>(null);
586+
587+
useEffect(() => {
588+
act(() => {
589+
ref.current?.onOpenBottomSheet();
590+
});
591+
}, []);
592+
593+
return (
594+
<PredictConsentSheet
595+
ref={ref}
596+
providerId={mockProviderId}
597+
onDismiss={mockOnDismiss}
598+
onAgree={mockOnAgree}
599+
/>
600+
);
601+
};
602+
603+
const { getByText } = render(<TestComponent />);
604+
605+
const learnMoreText = getByText('Learn more');
606+
const touchableParent = learnMoreText.parent;
607+
608+
expect(touchableParent).toBeTruthy();
609+
610+
act(() => {
611+
if (touchableParent) {
612+
fireEvent.press(touchableParent);
613+
}
614+
});
615+
616+
expect(mockRunAfterInteractions).toHaveBeenCalledTimes(1);
617+
const callback = runAfterInteractionsCallbacks[0];
618+
expect(callback).toBeDefined();
619+
620+
callback?.();
621+
622+
expect(mockNavigate).toHaveBeenCalledWith('Webview', {
623+
screen: 'SimpleWebview',
624+
params: {
625+
url: 'https://polymarket.com/tos',
626+
title: 'Terms and Conditions',
627+
},
628+
});
629+
});
630+
});
529631
});

app/components/UI/Predict/components/PredictConsentSheet/PredictConsentSheet.tsx

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import Text, {
44
TextVariant,
55
} from '../../../../../component-library/components/Texts/Text';
66
import { useTailwind } from '@metamask/design-system-twrnc-preset';
7-
import React, { forwardRef, useImperativeHandle } from 'react';
8-
import { Linking } from 'react-native';
7+
import React, { forwardRef, useCallback, useImperativeHandle } from 'react';
8+
import { InteractionManager, TouchableOpacity } from 'react-native';
99
import { strings } from '../../../../../../locales/i18n';
1010

1111
// Internal dependencies.
@@ -19,6 +19,7 @@ import {
1919
usePredictBottomSheet,
2020
type PredictBottomSheetRef,
2121
} from '../../hooks/usePredictBottomSheet';
22+
import { useNavigation } from '@react-navigation/native';
2223

2324
interface PredictConsentSheetProps {
2425
providerId: string;
@@ -33,6 +34,7 @@ const PredictConsentSheet = forwardRef<
3334
PredictConsentSheetProps
3435
>(({ providerId, onDismiss, onAgree }, ref) => {
3536
const tw = useTailwind();
37+
const navigation = useNavigation();
3638
const { acceptAgreement } = usePredictAgreement({ providerId });
3739
const { sheetRef, isVisible, closeSheet, handleSheetClosed, getRefHandlers } =
3840
usePredictBottomSheet({ onDismiss });
@@ -49,6 +51,18 @@ const PredictConsentSheet = forwardRef<
4951

5052
useImperativeHandle(ref, getRefHandlers, [getRefHandlers]);
5153

54+
const handlePolymarketTerms = useCallback(() => {
55+
InteractionManager.runAfterInteractions(() => {
56+
navigation.navigate('Webview', {
57+
screen: 'SimpleWebview',
58+
params: {
59+
url: 'https://polymarket.com/tos',
60+
title: strings('predict.consent_sheet.title'),
61+
},
62+
});
63+
});
64+
}, [navigation]);
65+
5266
if (!isVisible) {
5367
return null;
5468
}
@@ -66,19 +80,18 @@ const PredictConsentSheet = forwardRef<
6680
</Text>
6781
</BottomSheetHeader>
6882
<Box twClassName="px-6 pb-6">
69-
<Text variant={TextVariant.BodyMD} color={TextColor.Alternative}>
70-
{strings('predict.consent_sheet.description')}{' '}
71-
</Text>
72-
<Text
73-
variant={TextVariant.BodyMD}
74-
style={tw.style('text-info-default')}
75-
onPress={() => {
76-
Linking.openURL('https://polymarket.com/tos');
77-
}}
78-
suppressHighlighting
79-
>
80-
{strings('predict.consent_sheet.learn_more')}
81-
</Text>
83+
<TouchableOpacity onPress={handlePolymarketTerms}>
84+
<Text variant={TextVariant.BodyMD} color={TextColor.Alternative}>
85+
{strings('predict.consent_sheet.description')}{' '}
86+
<Text
87+
variant={TextVariant.BodyMD}
88+
style={tw.style('text-info-default')}
89+
suppressHighlighting
90+
>
91+
{strings('predict.consent_sheet.learn_more')}
92+
</Text>
93+
</Text>
94+
</TouchableOpacity>
8295
</Box>
8396
<BottomSheetFooter
8497
buttonPropsArray={[

app/components/UI/Predict/components/PredictDetailsChart/PredictDetailsChart.test.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,10 @@ describe('PredictDetailsChart', () => {
164164
});
165165

166166
it('renders loading state when isLoading is true', () => {
167-
const { getByText } = setupTest({ isLoading: true });
167+
const { getByTestId } = setupTest({ isLoading: true });
168168

169-
expect(getByText('Loading price history...')).toBeOnTheScreen();
169+
const lineChart = getByTestId('line-chart');
170+
expect(lineChart).toBeOnTheScreen();
170171
});
171172

172173
it('renders empty state when no data provided', () => {

0 commit comments

Comments
 (0)