diff --git a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx index fe672283bff..642bc2d3920 100644 --- a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx +++ b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx @@ -82,6 +82,9 @@ const StakeInputView = ({ route }: StakeInputViewProps) => { amountWei.toString(), activeAccount?.address as string, ); + navigation.navigate('StakeScreens', { + screen: Routes.STANDALONE_CONFIRMATIONS.STAKE_DEPOSIT, + }); return; } diff --git a/app/components/UI/Stake/routes/index.tsx b/app/components/UI/Stake/routes/index.tsx index 28f741c37bf..f71a6b624b7 100644 --- a/app/components/UI/Stake/routes/index.tsx +++ b/app/components/UI/Stake/routes/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import StakeInputView from '../Views/StakeInputView/StakeInputView'; import Routes from '../../../../constants/navigation/Routes'; +import { Confirm } from '../../../Views/confirmations/Confirm/Confirm'; import StakeConfirmationView from '../Views/StakeConfirmationView/StakeConfirmationView'; import UnstakeInputView from '../Views/UnstakeInputView/UnstakeInputView'; import UnstakeConfirmationView from '../Views/UnstakeConfirmationView/UnstakeConfirmationView'; @@ -11,6 +12,7 @@ import GasImpactModal from '../components/GasImpactModal'; import StakeEarningsHistoryView from '../Views/StakeEarningsHistoryView/StakeEarningsHistoryView'; import PoolStakingLearnMoreModal from '../components/PoolStakingLearnMoreModal'; import EarnTokenList from '../components/EarnTokenList'; + const Stack = createStackNavigator(); const ModalStack = createStackNavigator(); @@ -43,6 +45,10 @@ const StakeScreenStack = () => ( name={Routes.STAKING.EARNINGS_HISTORY} component={StakeEarningsHistoryView} /> + ); diff --git a/app/components/Views/confirmations/Confirm/Confirm.test.tsx b/app/components/Views/confirmations/Confirm/Confirm.test.tsx index 8b8422af628..74ff06e38bd 100644 --- a/app/components/Views/confirmations/Confirm/Confirm.test.tsx +++ b/app/components/Views/confirmations/Confirm/Confirm.test.tsx @@ -4,8 +4,8 @@ import renderWithProvider from '../../../../util/test/renderWithProvider'; import { personalSignatureConfirmationState, securityAlertResponse, - typedSignV1ConfirmationState, stakingDepositConfirmationState, + typedSignV1ConfirmationState, } from '../../../../util/test/confirm-data-helpers'; // eslint-disable-next-line import/no-namespace import * as ConfirmationRedesignEnabled from '../hooks/useConfirmationRedesignEnabled'; @@ -49,17 +49,11 @@ jest.mock('@react-navigation/native', () => ({ navigate: jest.fn(), addListener: jest.fn(), dispatch: jest.fn(), + setOptions: jest.fn(), }), })); describe('Confirm', () => { - it('renders flat confirmation', async () => { - const { getByTestId } = renderWithProvider(, { - state: stakingDepositConfirmationState, - }); - expect(getByTestId('flat-confirmation-container')).toBeDefined(); - }); - it('renders modal confirmation', async () => { const { getByTestId } = renderWithProvider(, { state: typedSignV1ConfirmationState, @@ -96,6 +90,18 @@ describe('Confirm', () => { expect(queryByText('This is a deceptive request')).toBeNull(); }); + it('renders correct information for staking deposit', async () => { + const { getByText } = renderWithProvider(, { + state: stakingDepositConfirmationState, + }); + expect(getByText('APR')).toBeDefined(); + expect(getByText('Est. annual reward')).toBeDefined(); + expect(getByText('Reward frequency')).toBeDefined(); + expect(getByText('Withdrawal time')).toBeDefined(); + expect(getByText('Network Fee')).toBeDefined(); + expect(getByText('Advanced details')).toBeDefined(); + }); + it('renders blockaid banner if confirmation has blockaid error response', async () => { const { getByText } = renderWithProvider(, { state: { diff --git a/app/components/Views/confirmations/Confirm/ConfirmRoot.test.tsx b/app/components/Views/confirmations/Confirm/ConfirmRoot.test.tsx index 9416c18db99..710737bb84a 100644 --- a/app/components/Views/confirmations/Confirm/ConfirmRoot.test.tsx +++ b/app/components/Views/confirmations/Confirm/ConfirmRoot.test.tsx @@ -24,19 +24,18 @@ describe('Confirm', () => { jest.clearAllMocks(); }); - it('renders flat confirmation', async () => { + it('navigates to modal confirmation', async () => { renderWithProvider(, { - state: stakingDepositConfirmationState, + state: personalSignatureConfirmationState, }); expect(mockNavigate).toHaveBeenCalledTimes(1); - expect(mockNavigate).toHaveBeenCalledWith(Routes.CONFIRM_FLAT_PAGE); + expect(mockNavigate).toHaveBeenLastCalledWith(Routes.CONFIRM_MODAL); }); - it('renders modal confirmation', async () => { + it('does not navigate if confirmation is standalone', async () => { renderWithProvider(, { - state: personalSignatureConfirmationState, + state: stakingDepositConfirmationState, }); - expect(mockNavigate).toHaveBeenCalledTimes(1); - expect(mockNavigate).toHaveBeenLastCalledWith(Routes.CONFIRM_MODAL); + expect(mockNavigate).not.toHaveBeenCalled(); }); }); diff --git a/app/components/Views/confirmations/Confirm/ConfirmRoot.tsx b/app/components/Views/confirmations/Confirm/ConfirmRoot.tsx index fafae47f092..6371672a417 100644 --- a/app/components/Views/confirmations/Confirm/ConfirmRoot.tsx +++ b/app/components/Views/confirmations/Confirm/ConfirmRoot.tsx @@ -4,19 +4,29 @@ import { useNavigation } from '@react-navigation/native'; import Routes from '../../../../constants/navigation/Routes'; import { useFlatConfirmation } from '../hooks/useFlatConfirmation'; import { useConfirmationRedesignEnabled } from '../hooks/useConfirmationRedesignEnabled'; +import { useStandaloneConfirmation } from '../hooks/useStandaloneConfirmation'; export const ConfirmRoot = () => { const { isRedesignedEnabled } = useConfirmationRedesignEnabled(); const { isFlatConfirmation } = useFlatConfirmation(); + const { isStandaloneConfirmation } = useStandaloneConfirmation(); const navigation = useNavigation(); useEffect(() => { if (isRedesignedEnabled) { + if (isStandaloneConfirmation) { + return; + } navigation.navigate( isFlatConfirmation ? Routes.CONFIRM_FLAT_PAGE : Routes.CONFIRM_MODAL, ); } - }, [isFlatConfirmation, isRedesignedEnabled, navigation]); + }, [ + isFlatConfirmation, + isRedesignedEnabled, + isStandaloneConfirmation, + navigation, + ]); return null; }; diff --git a/app/components/Views/confirmations/components/Confirm/Info/Info.test.tsx b/app/components/Views/confirmations/components/Confirm/Info/Info.test.tsx index 4d5ab480997..981857162c6 100644 --- a/app/components/Views/confirmations/components/Confirm/Info/Info.test.tsx +++ b/app/components/Views/confirmations/components/Confirm/Info/Info.test.tsx @@ -2,10 +2,7 @@ import React from 'react'; import { Text } from 'react-native'; import renderWithProvider from '../../../../../../util/test/renderWithProvider'; -import { - personalSignatureConfirmationState, - stakingDepositConfirmationState, -} from '../../../../../../util/test/confirm-data-helpers'; +import { personalSignatureConfirmationState } from '../../../../../../util/test/confirm-data-helpers'; // eslint-disable-next-line import/no-namespace import * as QRHardwareHook from '../../../context/QRHardwareContext/QRHardwareContext'; import Info from './Info'; @@ -60,12 +57,4 @@ describe('Info', () => { }); expect(getByText('QR Scanning Component')).toBeTruthy(); }); - describe('Staking Deposit', () => { - it('should render correctly', async () => { - const { getByText } = renderWithProvider(, { - state: stakingDepositConfirmationState, - }); - expect(getByText('Stake')).toBeDefined(); - }); - }); }); diff --git a/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/Navbar.test.tsx b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/Navbar.test.tsx new file mode 100644 index 00000000000..1b24a6409a7 --- /dev/null +++ b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/Navbar.test.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { render } from '@testing-library/react-native'; +import { getStakingDepositNavbar } from './Navbar'; + +describe('getStakingDepositNavbar', () => { + it('renders the header title correctly', () => { + const title = 'Test Title'; + const { getByText } = render( + <> + {getStakingDepositNavbar({ title, onReject: jest.fn() }).headerTitle()} + , + ); + + expect(getByText(title)).toBeTruthy(); + }); + + it('calls onReject when the back button is pressed', () => { + const onRejectMock = jest.fn(); + const { getByTestId } = render( + <> + {getStakingDepositNavbar({ + title: 'Test Title', + onReject: onRejectMock, + }).headerLeft()} + , + ); + + const backButton = getByTestId('staking-deposit-navbar-back-button'); + backButton.props.onPress(); + + expect(onRejectMock).toHaveBeenCalled(); + }); +}); diff --git a/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/Navbar.tsx b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/Navbar.tsx new file mode 100644 index 00000000000..4e889634580 --- /dev/null +++ b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/Navbar.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { + default as MorphText, + TextVariant, +} from '../../../../../../../component-library/components/Texts/Text'; +import ButtonIcon, { + ButtonIconSizes, +} from '../../../../../../../component-library/components/Buttons/ButtonIcon'; +import { IconName } from '../../../../../../../component-library/components/Icons/Icon'; + +export function getStakingDepositNavbar({ + title, + onReject, +}: { + title: string; + onReject: () => void; +}) { + const innerStyles = StyleSheet.create({ + headerLeft: { + marginHorizontal: 16, + }, + headerTitle: { + alignItems: 'center', + }, + }); + + function handleBackPress() { + if (onReject) { + onReject(); + } + } + + return { + headerTitle: () => ( + + {title} + + ), + headerLeft: () => ( + + ), + }; +} diff --git a/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.test.tsx b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.test.tsx new file mode 100644 index 00000000000..4d1f6492bee --- /dev/null +++ b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.test.tsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import renderWithProvider from '../../../../../../../util/test/renderWithProvider'; +import { stakingDepositConfirmationState } from '../../../../../../../util/test/confirm-data-helpers'; +import StakingDeposit from './StakingDeposit'; + +jest.mock('../../../../../../../core/Engine', () => ({ + getTotalFiatAccountBalance: () => ({ tokenFiat: 10 }), + context: { + NetworkController: { + getNetworkConfigurationByNetworkClientId: jest.fn(), + }, + GasFeeController: { + startPolling: jest.fn(), + stopPollingByPollingToken: jest.fn(), + }, + }, +})); + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useNavigation: () => ({ + navigate: jest.fn(), + setOptions: jest.fn(), + }), + }; +}); + +describe('StakingDeposit', () => { + it('should render correctly', () => { + const { getByText } = renderWithProvider(, { + state: stakingDepositConfirmationState, + }); + expect(getByText('APR')).toBeDefined(); + expect(getByText('Est. annual reward')).toBeDefined(); + expect(getByText('Reward frequency')).toBeDefined(); + expect(getByText('Withdrawal time')).toBeDefined(); + expect(getByText('Network Fee')).toBeDefined(); + expect(getByText('Advanced details')).toBeDefined(); + }); +}); diff --git a/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.tsx b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.tsx index 944668015c0..ee5f862f80f 100644 --- a/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.tsx +++ b/app/components/Views/confirmations/components/Confirm/Info/StakingDeposit/StakingDeposit.tsx @@ -1,18 +1,35 @@ -import React from 'react'; +import React, { useCallback, useEffect } from 'react'; +import { useNavigation } from '@react-navigation/native'; import { strings } from '../../../../../../../../locales/i18n'; import AdvancedDetails from '../../AdvancedDetails/AdvancedDetails'; -import FlatNavHeader from '../../FlatNavHeader'; import StakingDetails from '../../StakingDetails'; import TokenHero from '../../TokenHero'; import GasFeesDetails from '../GasFeesDetails'; +import { getStakingDepositNavbar } from './Navbar'; -const StakingDeposit = () => ( - <> - - - - - - -); +const StakingDeposit = () => { + const navigation = useNavigation(); + + const updateNavBar = useCallback(() => { + navigation.setOptions( + getStakingDepositNavbar({ + title: strings('stake.stake'), + onReject: () => navigation.goBack(), + }), + ); + }, [navigation]); + + useEffect(() => { + updateNavBar(); + }, [updateNavBar]); + + return ( + <> + + + + + + ); +}; export default StakingDeposit; diff --git a/app/components/Views/confirmations/components/Confirm/Title/Title.tsx b/app/components/Views/confirmations/components/Confirm/Title/Title.tsx index e66b207b084..21f10c3a168 100644 --- a/app/components/Views/confirmations/components/Confirm/Title/Title.tsx +++ b/app/components/Views/confirmations/components/Confirm/Title/Title.tsx @@ -10,6 +10,7 @@ import Text from '../../../../../../component-library/components/Texts/Text'; import useApprovalRequest from '../../../hooks/useApprovalRequest'; import { useSignatureRequest } from '../../../hooks/useSignatureRequest'; import { isSIWESignatureRequest , isRecognizedPermit, parseTypedDataMessageFromSignatureRequest } from '../../../utils/signature'; +import { useStandaloneConfirmation } from '../../../hooks/useStandaloneConfirmation'; import styleSheet from './Title.styles'; const getTitleAndSubTitle = (approvalRequest?: ApprovalRequest<{ data: string }>, signatureRequest?: SignatureRequest) => { @@ -60,6 +61,11 @@ const Title = () => { const { approvalRequest } = useApprovalRequest(); const signatureRequest = useSignatureRequest(); const { styles } = useStyles(styleSheet, {}); + const { isStandaloneConfirmation } = useStandaloneConfirmation(); + + if (isStandaloneConfirmation) { + return null; + } const { title, subTitle } = getTitleAndSubTitle(approvalRequest, signatureRequest); diff --git a/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.styles.ts b/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.styles.ts index ab07733b5b5..eacaa8d7600 100644 --- a/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.styles.ts +++ b/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.styles.ts @@ -12,8 +12,6 @@ const styleSheet = (params: { theme: Theme }) => { assetAmountText: { textAlign: 'center', }, - assetFiatConversionContainer: { - }, assetFiatConversionText: { textAlign: 'center', color: theme.colors.text.alternative, @@ -27,9 +25,10 @@ const styleSheet = (params: { theme: Theme }) => { width: 48, height: 48, }, - container:{ - paddingVertical: 8 - } + container: { + paddingBottom: 8, + paddingTop: 16, + }, }); }; diff --git a/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.tsx b/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.tsx index 6a7699573ee..c0147bea46b 100644 --- a/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.tsx +++ b/app/components/Views/confirmations/components/Confirm/TokenHero/TokenHero.tsx @@ -21,16 +21,16 @@ const NetworkAndTokenImage = ({ tokenSymbol: string; styles: StyleSheet.NamedStyles>; }) => ( - - - } - > - - - - ); + + + } + > + + + +); const AssetAmount = ({ tokenAmountDisplayValue, @@ -41,12 +41,12 @@ const AssetAmount = ({ tokenSymbol: string; styles: StyleSheet.NamedStyles>; }) => ( - - - {tokenAmountDisplayValue} {tokenSymbol} - - - ); + + + {tokenAmountDisplayValue} {tokenSymbol} + + +); const AssetFiatConversion = ({ fiatDisplayValue, @@ -55,12 +55,10 @@ const AssetFiatConversion = ({ fiatDisplayValue: string; styles: StyleSheet.NamedStyles>; }) => ( - - - {fiatDisplayValue} - - - ); + + {fiatDisplayValue} + +); const TokenHero = () => { const { styles } = useStyles(styleSheet, {}); diff --git a/app/components/Views/confirmations/hooks/useFlatConfirmation.ts b/app/components/Views/confirmations/hooks/useFlatConfirmation.ts index 2477d92a451..7dca46a0129 100644 --- a/app/components/Views/confirmations/hooks/useFlatConfirmation.ts +++ b/app/components/Views/confirmations/hooks/useFlatConfirmation.ts @@ -2,7 +2,6 @@ import { TransactionType } from '@metamask/transaction-controller'; import { useTransactionMetadataRequest } from '../hooks/useTransactionMetadataRequest'; -// todo: if possible derive way to dynamically check if confirmation should be rendered flat const FLAT_TRANSACTION_CONFIRMATIONS: TransactionType[] = [ TransactionType.stakingDeposit, ]; diff --git a/app/components/Views/confirmations/hooks/useStandaloneConfirmation.test.ts b/app/components/Views/confirmations/hooks/useStandaloneConfirmation.test.ts new file mode 100644 index 00000000000..db5f166e60b --- /dev/null +++ b/app/components/Views/confirmations/hooks/useStandaloneConfirmation.test.ts @@ -0,0 +1,24 @@ +import { + personalSignatureConfirmationState, + stakingDepositConfirmationState, +} from '../../../../util/test/confirm-data-helpers'; +import { renderHookWithProvider } from '../../../../util/test/renderWithProvider'; +import { useStandaloneConfirmation } from './useStandaloneConfirmation'; + +describe('useStandaloneConfirmation', () => { + it('returns true for staking confirmation', async () => { + const { result } = renderHookWithProvider(useStandaloneConfirmation, { + state: stakingDepositConfirmationState, + }); + + expect(result.current.isStandaloneConfirmation).toBe(true); + }); + + it('returns false for personal sign request', async () => { + const { result } = renderHookWithProvider(useStandaloneConfirmation, { + state: personalSignatureConfirmationState, + }); + + expect(result.current.isStandaloneConfirmation).toBe(false); + }); +}); diff --git a/app/components/Views/confirmations/hooks/useStandaloneConfirmation.ts b/app/components/Views/confirmations/hooks/useStandaloneConfirmation.ts new file mode 100644 index 00000000000..27e7ff1db8a --- /dev/null +++ b/app/components/Views/confirmations/hooks/useStandaloneConfirmation.ts @@ -0,0 +1,17 @@ +import { TransactionType } from '@metamask/transaction-controller'; +import { useTransactionMetadataRequest } from '../hooks/useTransactionMetadataRequest'; + +const STANDALONE_TRANSACTION_CONFIRMATIONS: TransactionType[] = [ + TransactionType.stakingDeposit, +]; + +export const useStandaloneConfirmation = () => { + const transactionMetadata = useTransactionMetadataRequest(); + + const isStandaloneConfirmation = + STANDALONE_TRANSACTION_CONFIRMATIONS.includes( + transactionMetadata?.type as TransactionType, + ); + + return { isStandaloneConfirmation }; +}; diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts index 1b4dc64f0e5..36a15f2c396 100644 --- a/app/constants/navigation/Routes.ts +++ b/app/constants/navigation/Routes.ts @@ -160,6 +160,9 @@ const Routes = { EARN_TOKEN_LIST: 'EarnTokenList', }, }, + STANDALONE_CONFIRMATIONS: { + STAKE_DEPOSIT: 'RedesignedStakeDeposit', + }, ///: BEGIN:ONLY_INCLUDE_IF(external-snaps) SNAPS: { SNAPS_SETTINGS_LIST: 'SnapsSettingsList',