Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: confirmations UI adjustments #10348

Merged
merged 16 commits into from
Sep 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Colors } from '../../../util/theme/models';

const createStyles = (colors: Colors) =>
StyleSheet.create({
inputWrapper: {
container: {
marginHorizontal: 16,
},
text: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import { Provider } from 'react-redux';
import { shallow } from 'enzyme';
import configureMockStore from 'redux-mock-store';
import Engine from '../../../core/Engine';

import renderWithProvider, {
DeepPartial,
Expand Down Expand Up @@ -52,7 +51,7 @@ jest.mock('../../../util/address', () => ({
...jest.requireActual('../../../util/address'),
isQRHardwareAccount: () => false,
}));
let mockGetERC20BalanceOf = jest.fn().mockReturnValue(0x0186a0);
const mockGetERC20BalanceOf = jest.fn().mockReturnValue(0x0186a0);

jest.mock('../../../core/Engine', () => ({
context: {
Expand Down Expand Up @@ -121,6 +120,7 @@ describe('AccountFromToInfoCard', () => {
it('should render correctly', () => {
const wrapper = shallow(
<Provider store={store}>
{/* @ts-expect-error: Rest props are ignored for testing purposes */}
<AccountFromToInfoCard transactionState={transactionState} />
</Provider>,
);
Expand All @@ -129,30 +129,16 @@ describe('AccountFromToInfoCard', () => {

it('should match snapshot', async () => {
const container = renderWithProvider(
//@ts-expect-error - Rest props are ignored for testing purposes
<AccountFromToInfoCard transactionState={transactionState} />,
{ state: mockInitialState },
);
expect(container).toMatchSnapshot();
});

it('should render from address', async () => {
const { findByText } = renderWithProvider(
<AccountFromToInfoCard transactionState={transactionState} />,
{ state: mockInitialState },
);
expect(await findByText('Account 1')).toBeDefined();
});

it('should render balance of from address', async () => {
const { findByText } = renderWithProvider(
<AccountFromToInfoCard transactionState={transactionState} />,
{ state: mockInitialState },
);
expect(await findByText('Balance: < 0.00001 ETH')).toBeDefined();
});

it('should render to account name', async () => {
const { findByText } = renderWithProvider(
//@ts-expect-error - Rest props are ignored for testing purposes
<AccountFromToInfoCard transactionState={transactionState} />,
{ state: mockInitialState },
);
Expand All @@ -161,6 +147,7 @@ describe('AccountFromToInfoCard', () => {

it('should render to address', async () => {
const { findByText } = renderWithProvider(
//@ts-expect-error - Rest props are ignored for testing purposes
<AccountFromToInfoCard transactionState={transactionState} />,
{ state: mockInitialState },
);
Expand All @@ -187,9 +174,8 @@ describe('AccountFromToInfoCard', () => {
transactionToName: '0xF4e8263979A89Dc357d7f9F79533Febc7f3e287B',
};
const { findByText } = renderWithProvider(
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<AccountFromToInfoCard transactionState={NFTTransaction as any} />,
//@ts-expect-error - Rest props are ignored for testing purposes
<AccountFromToInfoCard transactionState={NFTTransaction} />,
{ state: mockInitialState },
);
expect(await findByText('0xF4e8...287B')).toBeDefined();
Expand Down Expand Up @@ -217,66 +203,11 @@ describe('AccountFromToInfoCard', () => {
},
};
const { queryByText } = renderWithProvider(
//@ts-expect-error - Rest props are ignored for testing purposes
<AccountFromToInfoCard transactionState={txState} />,
{ state: mockInitialState },
);
expect(await queryByText('test1.eth')).toBeDefined();
expect(await queryByText('test3.eth')).toBeDefined();
});

describe('from account balance', () => {
const ERC20Transaction: Transaction = {
transactionFromName: 'a',
transactionToName: 'b',
selectedAsset: {
address: '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95',
decimals: 4,
image: 'https://metamask.github.io/test-dapp/metamask-fox.svg',
symbol: 'TST',
isETH: false,
},
transactionTo: '0x2f318c334780961fb129d2a6c30d0763d9a5c970',
transaction: {
data: '0xa9059cbb0000000000000000000000002f318c334780961fb129d2a6c30d0763d9a5c9700000000000000000000000000000000000000000000000000000000000003a98',
from: '0x519d2CE57898513F676a5C3b66496c3C394c9CC7',
to: '0x2f318c334780961fb129d2a6c30d0763d9a5c970',
},
};

beforeEach(() => {
jest.useFakeTimers();
mockGetERC20BalanceOf = jest.fn().mockReturnValue(0x0186a0);
Engine.context.AssetsContractController = {
getERC20BalanceOf: mockGetERC20BalanceOf,
} as Partial<AssetsContractController> as AssetsContractController;
});

it('should render balance from AssetsContractController.getERC20BalanceOf if selectedAddress is different from fromAddress', async () => {
const { findByText } = renderWithProvider(
<AccountFromToInfoCard transactionState={ERC20Transaction} />,
{ state: mockInitialState },
);
expect(mockGetERC20BalanceOf).toHaveBeenCalled();
expect(await findByText('Balance: 10 TST')).toBeDefined();
});

it('should render balance from TokenBalancesController.contractBalances if selectedAddress is same as fromAddress', async () => {
const transaction = {
...ERC20Transaction,
from: '0xe64dD0AB5ad7e8C5F2bf6Ce75C34e187af8b920A',
transaction: {
...ERC20Transaction.transaction,
from: '0xe64dD0AB5ad7e8C5F2bf6Ce75C34e187af8b920A',
},
};
const { findByText } = renderWithProvider(
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<AccountFromToInfoCard transactionState={transaction as any} />,
{ state: mockInitialState },
);
expect(mockGetERC20BalanceOf).toBeCalledTimes(0);
expect(await findByText('Balance: 0.0005 TST')).toBeDefined();
});
});
});
51 changes: 16 additions & 35 deletions app/components/UI/AccountFromToInfoCard/AccountFromToInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { connect } from 'react-redux';
import { Text, TouchableOpacity, View } from 'react-native';

import TransactionTypes from '../../../core/TransactionTypes';
import useAddressBalance from '../../../components/hooks/useAddressBalance/useAddressBalance';
import { strings } from '../../../../locales/i18n';
import {
selectChainId,
Expand All @@ -16,22 +15,16 @@ import { safeToChecksumAddress } from '../../../util/address';
import { useTheme } from '../../../util/theme';
import InfoModal from '../Swaps/components/InfoModal';
import useExistingAddress from '../../hooks/useExistingAddress';
import { AddressFrom, AddressTo } from '../AddressInputs';
import { AddressTo } from '../AddressInputs';
import createStyles from './AccountFromToInfoCard.styles';
import { AccountFromToInfoCardProps } from './AccountFromToInfoCard.types';
import { selectInternalAccounts } from '../../../selectors/accountsController';
import { toLowerCaseEquals } from '../../../util/general';
import { RootState } from '../../../reducers';
import AddressFrom from './AddressFrom';

const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => {
const {
internalAccounts,
chainId,
onPressFromAddressIcon,
ticker,
transactionState,
layout = 'horizontal',
} = props;
const { internalAccounts, chainId, ticker, transactionState, origin } = props;
const {
transaction: { from: rawFromAddress, data, to },
transactionTo,
Expand All @@ -51,10 +44,6 @@ const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => {
const existingToAddress = useExistingAddress(toAddress);
const { colors } = useTheme();
const styles = createStyles(colors);
const { addressBalance: fromAccountBalance } = useAddressBalance(
selectedAsset,
fromAddress,
);

useEffect(() => {
const fetchFromAccountDetails = async () => {
Expand Down Expand Up @@ -171,32 +160,24 @@ const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => {
existingToAddress === undefined && !!confusableCollection.length
}
isConfirmScreen
layout={layout}
layout="vertical"
toAddressName={toAccountName}
toSelectedAddress={toAddress}
/>
);

return (
<>
<View style={styles.inputWrapper}>
{fromAddress && (
<AddressFrom
fromAccountAddress={fromAddress}
fromAccountName={fromAccountName}
fromAccountBalance={fromAccountBalance}
layout={layout}
onPressIcon={onPressFromAddressIcon}
/>
)}
{existingToAddress === undefined && confusableCollection.length ? (
<TouchableOpacity onPress={() => setShowWarningModal(true)}>
{addressTo}
</TouchableOpacity>
) : (
addressTo
)}
</View>
<View style={styles.container}>
{fromAddress && (
<AddressFrom asset={selectedAsset} from={fromAddress} origin={origin} />
)}
{existingToAddress === undefined && confusableCollection.length ? (
<TouchableOpacity onPress={() => setShowWarningModal(true)}>
{addressTo}
</TouchableOpacity>
) : (
addressTo
)}
<InfoModal
body={
<Text style={styles.text}>
Expand All @@ -207,7 +188,7 @@ const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => {
title={strings('transaction.confusable_title')}
toggleModal={() => setShowWarningModal(!showWarningModal)}
/>
</>
</View>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ export interface AccountFromToInfoCardProps {
ticker?: string;
transactionState: Transaction;
layout?: string;
asset: SelectedAsset;
origin: string;
sdkDappMetadata?: {
url: string;
icon: string;
};
url: string;
}
40 changes: 40 additions & 0 deletions app/components/UI/AccountFromToInfoCard/AddressFrom.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Third party dependencies.
import { StyleSheet } from 'react-native';
import { Theme } from '../../../util/theme/models';
import { fontStyles } from '../../../styles/common';

/**
* Style sheet function for ModalConfirmation component.
*
* @param params Style sheet params.
* @param params.theme App theme from ThemeContext.
* @param params.vars Inputs that the style sheet depends on.
* @returns StyleSheet object.
*/
const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;
const { colors } = theme;
return StyleSheet.create({
container: {},
fromTextContainer: {
marginHorizontal: 8,
marginVertical: 8,
},
fromText: {
...fontStyles.normal,
color: colors.text.default,
fontSize: 16,
},
iconContainer: {
marginRight: 8,
},
domainUrl: {
...fontStyles.bold,
textAlign: 'center',
fontSize: 14,
color: colors.text.default,
},
});
};

export default styleSheet;
Loading
Loading