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

refactor: consolidate accounts references to a single source of truth #8664

Merged
merged 14 commits into from
Feb 27, 2024
85 changes: 67 additions & 18 deletions app/components/UI/WalletAccount/WalletAccount.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { createAccountSelectorNavDetails } from '../../../components/Views/Accou
// Internal dependencies
import WalletAccount from './WalletAccount';
import initialBackgroundState from '../../../util/test/initial-background-state.json';
import { Account } from '../../hooks/useAccounts';
import { KeyringTypes } from '@metamask/keyring-controller';

jest.mock('../../../core/Engine', () => ({
context: {
Expand All @@ -25,19 +27,21 @@ jest.mock('../../../core/Engine', () => ({
},
}));

const mockAccount: Account = {
name: 'Test account 1',
address: '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272',
type: KeyringTypes.hd,
yOffset: 0,
isSelected: true,
};

const mockInitialState = {
settings: {
useBlockieIcon: false,
},
engine: {
backgroundState: {
...initialBackgroundState,
PreferencesController: {
selectedAddress: '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272',
identities: {
'0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272': { name: 'Account 1' },
},
},
},
},
};
Expand Down Expand Up @@ -69,36 +73,81 @@ jest.mock('react-redux', () => ({

describe('WalletAccount', () => {
it('renders correctly', () => {
const { toJSON } = renderWithProvider(<WalletAccount />, {
state: mockInitialState,
});
const { toJSON } = renderWithProvider(
<WalletAccount account={mockAccount} />,
{
state: mockInitialState,
},
);
expect(toJSON()).toMatchSnapshot();
});

it('shows the account address', () => {
const { getByTestId } = renderWithProvider(<WalletAccount />, {
state: mockInitialState,
});
const { getByTestId } = renderWithProvider(
<WalletAccount account={mockAccount} />,
{
state: mockInitialState,
},
);
expect(getByTestId('wallet-account-address')).toBeDefined();
});

it('copies the account address to the clipboard when the copy button is pressed', async () => {
const { getByTestId } = renderWithProvider(<WalletAccount />, {
state: mockInitialState,
});
const { getByTestId } = renderWithProvider(
<WalletAccount account={mockAccount} />,
{
state: mockInitialState,
},
);

fireEvent.press(getByTestId('wallet-account-copy-button'));
expect(ClipboardManager.setString).toHaveBeenCalledTimes(1);
});

it('should navigate to the account selector screen on account press', () => {
const { getByTestId } = renderWithProvider(<WalletAccount />, {
state: mockInitialState,
});
const { getByTestId } = renderWithProvider(
<WalletAccount account={mockAccount} />,
{
state: mockInitialState,
},
);

fireEvent.press(getByTestId('account-picker'));
expect(mockNavigate).toHaveBeenCalledWith(
...createAccountSelectorNavDetails({}),
);
});
it('displays the correct account name', () => {
const { getByText } = renderWithProvider(
<WalletAccount account={mockAccount} />,
{
state: mockInitialState,
},
);
expect(getByText(mockAccount.name)).toBeDefined();
});
it('displays custom account name when ENS is defined but account name is not the default', () => {
const ensName = 'test.eth';
const { getByText } = renderWithProvider(
<WalletAccount account={mockAccount} ens={ensName} />,
{
state: mockInitialState,
},
);
expect(getByText(mockAccount.name)).toBeDefined();
});
it('displays ENS name when defined and account name is the default', () => {
const ensName = 'test.eth';
const mockAccountWithDefaultName: Account = {
...mockAccount,
name: 'Account 1',
};
const { getByText } = renderWithProvider(
<WalletAccount account={mockAccountWithDefaultName} ens={ensName} />,
{
state: mockInitialState,
},
);
expect(getByText(ensName)).toBeDefined();
});
});
54 changes: 6 additions & 48 deletions app/components/UI/WalletAccount/WalletAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
// Third parties dependencies
import React, {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react';
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import { useSelector } from 'react-redux';
import { useNavigation } from '@react-navigation/native';
import { Platform, View } from 'react-native';
Expand All @@ -18,15 +11,7 @@ import { createAccountSelectorNavDetails } from '../../../components/Views/Accou
import { useStyles } from '../../../component-library/hooks';
import generateTestId from '../../../../wdio/utils/generateTestId';
import AddressCopy from '../AddressCopy';
import {
doENSReverseLookup,
isDefaultAccountName,
} from '../../../util/ENSUtils';
import { selectChainId } from '../../../selectors/networkController';
import {
selectIdentities,
selectSelectedAddress,
} from '../../../selectors/preferencesController';
import { isDefaultAccountName } from '../../../util/ENSUtils';
import ButtonIcon from '../../../component-library/components/Buttons/ButtonIcon/ButtonIcon';
import { ButtonIconSizes } from '../../../component-library/components/Buttons/ButtonIcon';
import Routes from '../../../constants/navigation/Routes';
Expand All @@ -40,53 +25,26 @@ import {
} from '../../../../wdio/screen-objects/testIDs/Screens/WalletView.testIds';
import { getLabelTextByAddress } from '../../../util/address';

const WalletAccount = ({ style }: WalletAccountProps, ref: React.Ref<any>) => {
const WalletAccount = (
{ style, account, ens }: WalletAccountProps,
ref: React.Ref<any>,
) => {
const { styles } = useStyles(styleSheet, { style });

const { navigate } = useNavigation();
const [ens, setEns] = useState<string>();

const yourAccountRef = useRef(null);
const accountActionsRef = useRef(null);

useImperativeHandle(ref, () => ({
yourAccountRef,
accountActionsRef,
}));
/**
* A string that represents the selected address
*/
const selectedAddress = useSelector(selectSelectedAddress);

/**
* An object containing each identity in the format address => account
*/
const identities = useSelector(selectIdentities);

const chainId = useSelector(selectChainId);

const accountAvatarType = useSelector((state: any) =>
state.settings.useBlockieIcon
? AvatarAccountType.Blockies
: AvatarAccountType.JazzIcon,
);
const account = {
...identities[selectedAddress],
address: selectedAddress,
};

const lookupEns = useCallback(async () => {
try {
const accountEns = await doENSReverseLookup(account.address, chainId);

setEns(accountEns);
// eslint-disable-next-line no-empty
} catch {}
}, [account.address, chainId]);

useEffect(() => {
lookupEns();
}, [lookupEns]);

const onNavigateToAccountActions = () => {
navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
Expand Down
3 changes: 3 additions & 0 deletions app/components/UI/WalletAccount/WalletAccount.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { ViewStyle } from 'react-native';
import { Account } from '../../hooks/useAccounts';

export interface WalletAccountProps {
style?: ViewStyle;
account: Account;
ens?: string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ exports[`WalletAccount renders correctly 1`] = `
}
testID="account-label"
>
Account 1
Test account 1
</Text>
</View>
</View>
Expand Down Expand Up @@ -278,9 +278,7 @@ exports[`WalletAccount renders correctly 1`] = `
}
}
testID="wallet-account-address"
>
0xC495...D272
</Text>
/>
<SvgMock
color="#0376C9"
height={16}
Expand Down
4 changes: 2 additions & 2 deletions app/components/Views/ChoosePassword/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { LoginOptionsSwitch } from '../../UI/LoginOptionsSwitch';
import navigateTermsOfUse from '../../../util/termsOfUse/termsOfUse';
import { ChoosePasswordSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ChoosePassword.selectors';
import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding';
import { selectSelectedAddress } from '../../../selectors/preferencesController';

const createStyles = (colors) =>
StyleSheet.create({
Expand Down Expand Up @@ -789,8 +790,7 @@ class ChoosePassword extends PureComponent {
ChoosePassword.contextType = ThemeContext;

const mapStateToProps = (state) => ({
selectedAddress:
state.engine.backgroundState.PreferencesController?.selectedAddress,
selectedAddress: selectSelectedAddress(state),
});

const mapDispatchToProps = (dispatch) => ({
Expand Down
9 changes: 9 additions & 0 deletions app/components/Views/Wallet/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ jest.mock('../../../core/Engine', () => ({
AccountTrackerController: {
refresh: jest.fn(),
},
KeyringController: {
state: {
keyrings: [
{
accounts: ['0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272'],
},
],
},
},
},
}));

Expand Down
56 changes: 46 additions & 10 deletions app/components/Views/Wallet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
import { selectAccountsByChainId } from '../../../selectors/accountTrackerController';
import { selectSelectedAddress } from '../../../selectors/preferencesController';
import { useMetrics } from '../../../components/hooks/useMetrics';
import { useAccounts } from '../../hooks/useAccounts';

const createStyles = ({ colors, typography }: Theme) =>
StyleSheet.create({
Expand Down Expand Up @@ -124,6 +125,32 @@ const Wallet = ({ navigation }: any) => {
*/
const providerConfig = useSelector(selectProviderConfig);

/**
* A list of all the user accounts and a mapping of ENS name to account address if they exist
*/
const { accounts, ensByAccountAddress } = useAccounts();

/**
* An object representing the currently selected account.
*/
const selectedAccount = useMemo(() => {
if (accounts.length > 0) {
return accounts.find((account) => account.isSelected);
}
return undefined;
}, [accounts]);

owencraston marked this conversation as resolved.
Show resolved Hide resolved
/**
* ENS name for the currently selected account.
* This value may be undefined if there is no corresponding ENS name for the account.
*/
const ensForSelectedAccount = useMemo(() => {
if (ensByAccountAddress && selectedAccount) {
return ensByAccountAddress[selectedAccount.address];
}
return undefined;
}, [ensByAccountAddress, selectedAccount]);

const networkName = useMemo(
() => getNetworkNameFromProviderConfig(providerConfig),
[providerConfig],
Expand Down Expand Up @@ -274,8 +301,14 @@ const Wallet = ({ navigation }: any) => {
}
return (
<View style={styles.wrapper}>
<WalletAccount style={styles.walletAccount} ref={walletRef} />

{selectedAccount ? (
owencraston marked this conversation as resolved.
Show resolved Hide resolved
<WalletAccount
account={selectedAccount}
ens={ensForSelectedAccount}
style={styles.walletAccount}
ref={walletRef}
/>
) : null}
<ScrollableTabView
renderTabBar={renderTabBar}
// eslint-disable-next-line react/jsx-no-bind
Expand Down Expand Up @@ -304,17 +337,20 @@ const Wallet = ({ navigation }: any) => {
</View>
);
}, [
tokens,
accountsByChainId,
providerConfig.chainId,
selectedAddress,
styles.wrapper,
styles.walletAccount,
selectedAccount,
ensForSelectedAccount,
renderTabBar,
conversionRate,
currentCurrency,
navigation,
onChangeTab,
selectedAddress,
navigation,
ticker,
tokens,
styles,
providerConfig.chainId,
accountsByChainId,
conversionRate,
currentCurrency,
]);
const renderLoader = useCallback(
() => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import withQRHardwareAwareness from '../../../../UI/QRHardware/withQRHardwareAwa
import WebsiteIcon from '../../../../UI/WebsiteIcon';
import { ResultType } from '../BlockaidBanner/BlockaidBanner.types';
import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
import { selectSelectedAddress } from '../../../../../selectors/preferencesController';

const createStyles = (colors) =>
StyleSheet.create({
Expand Down Expand Up @@ -417,8 +418,7 @@ class SignatureRequest extends PureComponent {
}

const mapStateToProps = (state) => ({
selectedAddress:
state.engine.backgroundState.PreferencesController.selectedAddress,
selectedAddress: selectSelectedAddress(state),
networkType: selectProviderType(state),
securityAlertResponse: state.signatureRequest.securityAlertResponse,
});
Expand Down
Loading
Loading