Skip to content

Commit

Permalink
Merge branch 'main' of github.com:MetaMask/metamask-mobile into brian…
Browse files Browse the repository at this point in the history
…/assets-controllers-43
  • Loading branch information
bergeron committed Nov 20, 2024
2 parents 76159ba + 8c26ada commit f6f7646
Show file tree
Hide file tree
Showing 35 changed files with 1,551 additions and 134 deletions.
33 changes: 30 additions & 3 deletions app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -686,9 +686,36 @@ const BuildQuote = () => {
);
}

const displayAmount = isBuy
? formatAmount(amountNumber)
: `${amount} ${selectedAsset?.symbol}`;
// If the current view is for Sell the amount (crypto) is displayed as is
let displayAmount = `${amount} ${selectedAsset?.symbol}`;

// If the current ivew is for Buy we will format the amount
if (isBuy) {
// Split the amount to detect if it has decimals
const splitAmount = amount.split(/(\.)|(,)/);
// If the splitAmount array has more than 1 element it means that the amount has decimals
// For example:
// 100.50 -> splitAmount = ['100', '.', undefined, '50']
// 100,50 -> splitAmount = ['100', undefined, ',', '50']
// Note: this help us capture the input separator (dot or comma)
const hasDecimalsSplit = splitAmount.length > 1;

displayAmount =
isBuy && amountFocused
? // If the amount is focused (being edited) the amount integer part will be shown in groups separated by spaces
`${formatAmount(Math.trunc(amountNumber), true)}${
// If the amount has decimals the decimal part will be shown
// using the separator and the decimal part
// Note, the decimal part will be displayed even if it is being typed (ends with a separator or 0)
hasDecimalsSplit
? `${splitAmount[1] ?? splitAmount[2] ?? ''}${
splitAmount[3] ?? ''
}`
: ''
}`
: // If the amount is not focused it will be fully formatted
formatAmount(amountNumber);
}

let quickAmounts: QuickAmount[] = [];

Expand Down
12 changes: 10 additions & 2 deletions app/components/UI/Ramp/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,17 @@ export const formatId = (id: string) => {
return id.startsWith('/') ? id : '/' + id;
};

export function formatAmount(amount: number) {
export function formatAmount(amount: number, useParts = false) {
try {
if (Intl?.NumberFormat) return new Intl.NumberFormat().format(amount);
if (Intl?.NumberFormat) {
if (useParts) {
return new Intl.NumberFormat()
.formatToParts(amount)
.map(({ type, value }) => (type === 'integer' ? value : ''))
.join(' ');
}
return new Intl.NumberFormat().format(amount);
}
return String(amount);
} catch (e) {
return String(amount);
Expand Down
4 changes: 4 additions & 0 deletions app/components/Views/LedgerConnect/index.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ const createStyles = (colors: Colors) =>
flex: 1,
marginTop: Device.getDeviceHeight() * 0.025,
},
bodyContainerWhithErrorMessage: {
flex: 1,
marginTop: Device.getDeviceHeight() * 0.01,
},
textContainer: {
marginTop: Device.getDeviceHeight() * 0.05,
},
Expand Down
29 changes: 25 additions & 4 deletions app/components/Views/LedgerConnect/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useBluetooth from '../../hooks/Ledger/useBluetooth';
import useBluetoothDevices, {
BluetoothDevice,
} from '../../hooks/Ledger/useBluetoothDevices';
import { fireEvent } from '@testing-library/react-native';
import { fireEvent, waitFor } from '@testing-library/react-native';
import {
useNavigation,
NavigationProp,
Expand Down Expand Up @@ -35,9 +35,6 @@ interface UseBluetoothDevicesHook {
deviceScanError: boolean;
}

jest.mock('../../hooks/useBluetoothPermissions');
jest.mock('../../hooks/Ledger/useBluetooth');
jest.mock('../../hooks/Ledger/useBluetoothDevices');
jest.mock('../../hooks/Ledger/useLedgerBluetooth');
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
Expand All @@ -62,6 +59,11 @@ jest.mock('../../../util/device', () => ({
getDeviceHeight: jest.fn(),
}));

jest.mock('../../../core/Ledger/Ledger', () => ({
...jest.requireActual('../../../core/Ledger/Ledger'),
getDeviceId: jest.fn().mockResolvedValue('device-id'),
}));

jest.mock('../../../core/Engine', () => ({
context: {
KeyringController: {
Expand Down Expand Up @@ -366,4 +368,23 @@ describe('LedgerConnect', () => {

expect(ledgerLogicToRun).toHaveBeenCalled();
});

it('shows error message about multiple devices support', async () => {
isSendingLedgerCommands = true;
isAppLaunchConfirmationNeeded = false;
const { getByTestId } = renderWithProvider(
<LedgerConnect
onConnectLedger={onConfirmationComplete}
isSendingLedgerCommands={isSendingLedgerCommands}
isAppLaunchConfirmationNeeded={isAppLaunchConfirmationNeeded}
ledgerLogicToRun={ledgerLogicToRun}
ledgerError={undefined}
selectedDevice={selectedDevice}
setSelectedDevice={setSelectedDevice}
/>,
);
await waitFor(() => {
expect(getByTestId('multiple-devices-error-message')).toBeDefined();
});
});
});
42 changes: 35 additions & 7 deletions app/components/Views/LedgerConnect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ import { getSystemVersion } from 'react-native-device-info';
import { LedgerCommunicationErrors } from '../../../core/Ledger/ledgerErrors';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import createStyles from './index.styles';
import { BluetoothInterface } from '../../hooks/Ledger/useBluetoothDevices';
import {
BluetoothDevice,
BluetoothInterface,
} from '../../hooks/Ledger/useBluetoothDevices';
import { getDeviceId } from '../../../core/Ledger/Ledger';

interface LedgerConnectProps {
onConnectLedger: () => void;
Expand Down Expand Up @@ -62,9 +66,9 @@ const LedgerConnect = ({
const styles = useMemo(() => createStyles(theme.colors), [theme]);
const [errorDetail, setErrorDetails] = useState<LedgerConnectionErrorProps>();
const [loading, setLoading] = useState(false);
const [hasMatchingDeviceId, setHasMatchingDeviceId] = useState(true);
const [retryTimes, setRetryTimes] = useState(0);
const dispatch = useDispatch();

const deviceOSVersion = Number(getSystemVersion()) || 0;

useEffect(() => {
Expand All @@ -80,6 +84,20 @@ const LedgerConnect = ({
});
};

const onDeviceSelected = (currentDevice: BluetoothDevice | undefined) => {
const getStoredDeviceId = async () => {
const storedDeviceId = await getDeviceId();
const isMatchingDeviceId =
!storedDeviceId || currentDevice?.id === storedDeviceId;
setHasMatchingDeviceId(isMatchingDeviceId);

if (isMatchingDeviceId) {
setSelectedDevice(currentDevice);
}
};
getStoredDeviceId();
};

const handleErrorWithRetry = (errorTitle: string, errorSubtitle: string) => {
setErrorDetails({
errorTitle,
Expand Down Expand Up @@ -111,6 +129,11 @@ const LedgerConnect = ({
});
};

const getStylesWithMultipleDevicesErrorMessage = () =>
hasMatchingDeviceId
? styles.bodyContainer
: styles.bodyContainerWhithErrorMessage;

useEffect(() => {
if (ledgerError) {
setLoading(false);
Expand Down Expand Up @@ -254,13 +277,16 @@ const LedgerConnect = ({
<Text bold>{strings('ledger.open_eth_app_message_two')} </Text>
</Text>
)}
{!hasMatchingDeviceId && (
<Text red small testID={'multiple-devices-error-message'}>
{strings('ledger.multiple_devices_error_message')}
</Text>
)}
</View>
<View style={styles.bodyContainer}>
<View style={getStylesWithMultipleDevicesErrorMessage()}>
{!isAppLaunchConfirmationNeeded ? (
<Scan
onDeviceSelected={(ledgerDevice) =>
setSelectedDevice(ledgerDevice)
}
onDeviceSelected={onDeviceSelected}
onScanningErrorStateChanged={(error) => setErrorDetails(error)}
ledgerError={ledgerError}
/>
Expand All @@ -271,7 +297,9 @@ const LedgerConnect = ({
type="confirm"
onPress={connectLedger}
testID={'add-network-button'}
disabled={loading || isSendingLedgerCommands}
disabled={
!hasMatchingDeviceId || loading || isSendingLedgerCommands
}
>
{loading || isSendingLedgerCommands ? (
<ActivityIndicator color={styles.loader.color} />
Expand Down
12 changes: 11 additions & 1 deletion app/components/Views/confirmations/Confirm/Confirm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';

import renderWithProvider from '../../../../util/test/renderWithProvider';
import { personalSignatureConfirmationState } from '../../../../util/test/confirm-data-helpers';
import {
personalSignatureConfirmationState,
typedSignV1ConfirmationState,
} from '../../../../util/test/confirm-data-helpers';
import Confirm from './index';

describe('Confirm', () => {
Expand All @@ -11,4 +14,11 @@ describe('Confirm', () => {
});
expect(container).toMatchSnapshot();
});

it('should match snapshot for typed sign v1', async () => {
const container = renderWithProvider(<Confirm />, {
state: typedSignV1ConfirmationState,
});
expect(container).toMatchSnapshot();
});
});
Loading

0 comments on commit f6f7646

Please sign in to comment.