Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,180 changes: 1,163 additions & 17 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,4 @@ dependencies {
androidTestImplementation "net.java.dev.jna:jna:5.12.1"
androidTestImplementation "net.java.dev.jna:jna-platform:5.12.1"
androidTestImplementation "org.opentest4j:opentest4j:1.2.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,22 @@ const PerpsCancelAllOrdersView: React.FC<PerpsCancelAllOrdersViewProps> = ({
}
}, [navigation, externalSheetRef, sheetRef, onExternalClose]);

// Wrapper for "Keep Orders" button that properly handles overlay dismissal
const handleKeepButtonPress = useCallback(() => {
if (externalSheetRef) {
// When used as overlay, close the sheet properly to remove overlay
handleClose();
} else {
// When used as standalone screen, use hook's navigation
handleKeepOrders();
}
}, [externalSheetRef, handleClose, handleKeepOrders]);

const footerButtons = useMemo(
() => [
{
label: strings('perps.cancel_all_modal.keep_orders'),
onPress: handleKeepOrders,
onPress: handleKeepButtonPress,
variant: ButtonVariants.Secondary,
size: ButtonSize.Lg,
disabled: isCanceling,
Expand All @@ -197,13 +208,17 @@ const PerpsCancelAllOrdersView: React.FC<PerpsCancelAllOrdersViewProps> = ({
danger: true,
},
],
[handleKeepOrders, handleCancelAll, isCanceling],
[handleKeepButtonPress, handleCancelAll, isCanceling],
);

// Show empty state if no orders (WebSocket data loads instantly, no loading state needed)
if (!orders || orders.length === 0) {
return (
<BottomSheet ref={sheetRef} shouldNavigateBack={!externalSheetRef}>
<BottomSheet
ref={sheetRef}
shouldNavigateBack={!externalSheetRef}
onClose={externalSheetRef ? onExternalClose : undefined}
>
<BottomSheetHeader onClose={handleClose}>
<Text variant={TextVariant.HeadingMD}>
{strings('perps.cancel_all_modal.title')}
Expand All @@ -219,7 +234,11 @@ const PerpsCancelAllOrdersView: React.FC<PerpsCancelAllOrdersViewProps> = ({
}

return (
<BottomSheet ref={sheetRef} shouldNavigateBack={!externalSheetRef}>
<BottomSheet
ref={sheetRef}
shouldNavigateBack={!externalSheetRef}
onClose={externalSheetRef ? onExternalClose : undefined}
>
<BottomSheetHeader onClose={handleClose}>
<Text variant={TextVariant.HeadingMD}>
{strings('perps.cancel_all_modal.title')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,22 @@ const PerpsCloseAllPositionsView: React.FC<PerpsCloseAllPositionsViewProps> = ({
}
}, [navigation, externalSheetRef, sheetRef, onExternalClose]);

// Wrapper for "Keep Positions" button that properly handles overlay dismissal
const handleKeepButtonPress = useCallback(() => {
if (externalSheetRef) {
// When used as overlay, close the sheet properly to remove overlay
handleClose();
} else {
// When used as standalone screen, use hook's navigation
handleKeepPositions();
}
}, [externalSheetRef, handleClose, handleKeepPositions]);

const footerButtons = useMemo(
() => [
{
label: strings('perps.close_all_modal.keep_positions'),
onPress: handleKeepPositions,
onPress: handleKeepButtonPress,
variant: ButtonVariants.Secondary,
size: ButtonSize.Lg,
disabled: isClosing,
Expand All @@ -224,13 +235,17 @@ const PerpsCloseAllPositionsView: React.FC<PerpsCloseAllPositionsViewProps> = ({
danger: true,
},
],
[handleKeepPositions, handleCloseAll, isClosing],
[handleKeepButtonPress, handleCloseAll, isClosing],
);

// Show loading state while fetching positions
if (isInitialLoading) {
return (
<BottomSheet ref={sheetRef} shouldNavigateBack={!externalSheetRef}>
<BottomSheet
ref={sheetRef}
shouldNavigateBack={!externalSheetRef}
onClose={externalSheetRef ? onExternalClose : undefined}
>
<BottomSheetHeader onClose={handleClose}>
<Text variant={TextVariant.HeadingMD}>
{strings('perps.close_all_modal.title')}
Expand All @@ -249,7 +264,11 @@ const PerpsCloseAllPositionsView: React.FC<PerpsCloseAllPositionsViewProps> = ({
// Show empty state if no positions
if (!positions || positions.length === 0) {
return (
<BottomSheet ref={sheetRef} shouldNavigateBack={!externalSheetRef}>
<BottomSheet
ref={sheetRef}
shouldNavigateBack={!externalSheetRef}
onClose={externalSheetRef ? onExternalClose : undefined}
>
<BottomSheetHeader onClose={handleClose}>
<Text variant={TextVariant.HeadingMD}>
{strings('perps.close_all_modal.title')}
Expand All @@ -265,7 +284,11 @@ const PerpsCloseAllPositionsView: React.FC<PerpsCloseAllPositionsViewProps> = ({
}

return (
<BottomSheet ref={sheetRef} shouldNavigateBack={!externalSheetRef}>
<BottomSheet
ref={sheetRef}
shouldNavigateBack={!externalSheetRef}
onClose={externalSheetRef ? onExternalClose : undefined}
>
<BottomSheetHeader onClose={handleClose}>
<Text variant={TextVariant.HeadingMD}>
{strings('perps.close_all_modal.title')}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fireEvent, render } from '@testing-library/react-native';
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import { PerpsAmountDisplaySelectorsIDs } from '../../../../../../e2e/selectors/Perps/Perps.selectors';
import PredictAmountDisplay from './PredictAmountDisplay';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const PredictAmountDisplay: React.FC<PredictAmountDisplayProps> = ({
<Animated.View
testID="cursor"
style={[
tw.style('w-0.5 h-13.5 ml-1'),
tw.style(`w-0.5 h-[${lineHeight - 4}px] ml-0.5`),
{
opacity: fadeAnim,
backgroundColor: colors.text.default,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import React from 'react';
import { Box } from '@metamask/design-system-react-native';
import React from 'react';
import { strings } from '../../../../../../locales/i18n';
import Text, {
TextColor,
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
import { formatPrice } from '../../utils/format';
import ButtonIcon, {
ButtonIconSizes,
} from '../../../../../component-library/components/Buttons/ButtonIcon';
import {
IconColor,
IconName,
} from '../../../../../component-library/components/Icons/Icon';
import { strings } from '../../../../../../locales/i18n';

interface PredictFeeSummaryProps {
disabled: boolean;
Expand All @@ -37,11 +30,6 @@ const PredictFeeSummary: React.FC<PredictFeeSummaryProps> = ({
<Text color={TextColor.Alternative} variant={TextVariant.BodyMD}>
{strings('predict.fee_summary.provider_fee')}
</Text>
<ButtonIcon
iconName={IconName.Info}
size={ButtonIconSizes.Sm}
iconColor={IconColor.Alternative}
/>
</Box>
<Text color={TextColor.Alternative}>
{formatPrice(providerFee, {
Expand All @@ -54,11 +42,6 @@ const PredictFeeSummary: React.FC<PredictFeeSummaryProps> = ({
<Text color={TextColor.Alternative} variant={TextVariant.BodyMD}>
{strings('predict.fee_summary.metamask_fee')}
</Text>
<ButtonIcon
iconName={IconName.Info}
size={ButtonIconSizes.Sm}
iconColor={IconColor.Alternative}
/>
</Box>
<Text color={TextColor.Alternative}>
{formatPrice(metamaskFee, {
Expand All @@ -71,11 +54,6 @@ const PredictFeeSummary: React.FC<PredictFeeSummaryProps> = ({
<Text color={TextColor.Alternative} variant={TextVariant.BodyMD}>
{strings('predict.fee_summary.total')}
</Text>
<ButtonIcon
iconName={IconName.Info}
size={ButtonIconSizes.Sm}
iconColor={IconColor.Alternative}
/>
</Box>
<Text color={TextColor.Alternative}>
{formatPrice(total, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ const styleSheet = () =>
width: '100%',
flex: 5,
},
positionImageContainer: {
paddingTop: 4,
},
positionImage: {
width: 40,
height: 40,
borderRadius: 100,
alignSelf: 'center',
alignSelf: 'flex-start',
},
positionPnl: {
flexDirection: 'column',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,11 @@ const PredictPosition: React.FC<PredictPositionProps> = ({
style={styles.positionContainer}
onPress={() => onPress?.(position)}
>
<View style={styles.positionImage}>
<View style={styles.positionImageContainer}>
<Image source={{ uri: icon }} style={styles.positionImage} />
</View>
<View style={styles.positionDetails}>
<Text
variant={TextVariant.BodyMDMedium}
color={TextColor.Default}
numberOfLines={1}
ellipsizeMode="tail"
>
<Text variant={TextVariant.BodyMDMedium} color={TextColor.Default}>
{title}
</Text>
<Text variant={TextVariant.BodySMMedium} color={TextColor.Alternative}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,8 @@ declare global {
}

jest.mock('../../../../../../locales/i18n', () => ({
strings: (key: string, vars?: Record<string, string | number>) => {
strings: (key: string, _vars?: Record<string, string | number>) => {
switch (key) {
case 'predict.market_details.amount_on_outcome':
return `$${vars?.amount} on ${vars?.outcome}`;
case 'predict.market_details.outcome_at_price':
return `${vars?.outcome} at ${vars?.price}¢`;
case 'predict.market_details.won':
return 'Won';
case 'predict.market_details.lost':
Expand Down Expand Up @@ -189,8 +185,10 @@ describe('PredictPositionDetail', () => {
it('renders open position with current value, percent change and cash out', () => {
renderComponent();

expect(screen.getByText('$123.45 on Yes')).toBeOnTheScreen();
expect(screen.getByText('Yes at 34¢')).toBeOnTheScreen();
expect(screen.getByText('Group')).toBeOnTheScreen();
expect(
screen.getByText('$123.45 on Yes • 34¢', { exact: false }),
).toBeOnTheScreen();

expect(screen.getByText('$2,345.67')).toBeOnTheScreen();
expect(screen.getByText('+5.25%')).toBeOnTheScreen();
Expand All @@ -210,8 +208,10 @@ describe('PredictPositionDetail', () => {
it('renders initial value line and avgPrice cents', () => {
renderComponent({ initialValue: 50, outcome: 'No', avgPrice: 0.7 });

expect(screen.getByText('$50.00 on No')).toBeOnTheScreen();
expect(screen.getByText('No at 70¢')).toBeOnTheScreen();
expect(screen.getByText('Group')).toBeOnTheScreen();
expect(
screen.getByText('$50.00 on No • 70¢', { exact: false }),
).toBeOnTheScreen();
});

it('renders won result with current value when market is closed and percent positive', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ const PredictPosition: React.FC<PredictPositionProps> = ({
marketStatus,
}: PredictPositionProps) => {
const tw = useTailwind();
const { icon, initialValue, percentPnl, outcome, avgPrice, currentValue } =
position;

const {
icon,
initialValue,
percentPnl,
outcome,
avgPrice,
currentValue,
title,
} = position;
const navigation =
useNavigation<NavigationProp<PredictNavigationParamList>>();
const { navigate } = navigation;
Expand All @@ -47,6 +55,10 @@ const PredictPosition: React.FC<PredictPositionProps> = ({
navigation,
});

const groupItemTitle = market?.outcomes.find(
(o) => o.id === position.outcomeId && o.groupItemTitle,
)?.groupItemTitle;

const onCashOut = () => {
executeGuardedAction(() => {
const _outcome = market?.outcomes.find(
Expand Down Expand Up @@ -91,38 +103,34 @@ const PredictPosition: React.FC<PredictPositionProps> = ({
};

return (
<Box twClassName="w-full p-4 mb-4 gap-3 bg-background-muted rounded-xl">
<Box twClassName="w-full p-4 mb-4 gap-3 bg-background-muted rounded-xl justify-between">
<Box twClassName="flex-row items-start gap-4">
{Boolean(icon) && (
<Image
source={{ uri: icon }}
resizeMode="cover"
style={tw.style('w-10 h-10 rounded-lg self-center')}
/>
<Box twClassName="w-10 h-10 self-start mt-1">
<Image
source={{ uri: icon }}
resizeMode="cover"
style={tw.style('w-full h-full rounded-lg')}
/>
</Box>
)}
<Box>
<Box twClassName="flex-1">
<Text
variant={TextVariant.BodyMDMedium}
color={TextColor.Default}
numberOfLines={1}
ellipsizeMode="tail"
>
{strings('predict.market_details.amount_on_outcome', {
amount: initialValue.toFixed(2),
outcome,
})}
{groupItemTitle ?? title}
</Text>
<Text
variant={TextVariant.BodySMMedium}
color={TextColor.Alternative}
>
{strings('predict.market_details.outcome_at_price', {
outcome,
price: (avgPrice * 100).toFixed(0),
})}
${initialValue.toFixed(2)} on {outcome} •{' '}
{(avgPrice * 100).toFixed(0)}¢
</Text>
</Box>
<Box twClassName="items-end justify-end ml-auto">
<Box twClassName="items-end justify-end ml-auto shrink-0">
{renderValueText()}
{marketStatus === PredictMarketStatus.OPEN && (
<Text
Expand Down
Loading
Loading