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
20 changes: 20 additions & 0 deletions app/components/Nav/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ import RevealSRP from '../../Views/MultichainAccounts/sheets/RevealSRP';
import { DeepLinkModal } from '../../UI/DeepLinkModal';
import { checkForDeeplink } from '../../../actions/user';
import { WalletDetails } from '../../Views/MultichainAccounts/WalletDetails/WalletDetails';
import { SmartAccountUpdateModal } from '../../Views/confirmations/components/smart-account-update-modal';

const clearStackNavigatorOptions = {
headerShown: false,
Expand Down Expand Up @@ -656,6 +657,21 @@ const ModalSwitchAccountType = () => (
</Stack.Navigator>
);

const ModalSmartAccountOptIn = () => (
<Stack.Navigator
screenOptions={{
headerShown: false,
cardStyle: { backgroundColor: importedColors.transparent },
}}
mode={'modal'}
>
<Stack.Screen
name={Routes.SMART_ACCOUNT_OPT_IN}
component={SmartAccountUpdateModal}
/>
</Stack.Navigator>
);

const AppFlow = () => {
const userLoggedIn = useSelector(selectUserLoggedIn);

Expand Down Expand Up @@ -796,6 +812,10 @@ const AppFlow = () => {
name={Routes.CONFIRMATION_SWITCH_ACCOUNT_TYPE}
component={ModalSwitchAccountType}
/>
<Stack.Screen
name={Routes.SMART_ACCOUNT_OPT_IN}
component={ModalSmartAccountOptIn}
/>
</Stack.Navigator>
);
};
Expand Down
3 changes: 2 additions & 1 deletion app/components/UI/Carousel/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import cashoutImage from '../../../images/banners/banner_image_cashout.png';
import aggregatedImage from '../../../images/banners/banner_image_aggregated.png';
import backupAndSyncImage from '../../../images/banners/banner_image_backup_and_sync.png';
import multiSrpImage from '../../../images/banners/banner_image_multisrp.png';
import { createSmartAccountNavigationDetails } from '../../Views/confirmations/utils/generic';
///: BEGIN:ONLY_INCLUDE_IF(solana)
import solanaImage from '../../../images/banners/banner_image_solana.png';
import { WalletClientType } from '../../../core/SnapKeyring/MultichainWalletSnapClient';
Expand Down Expand Up @@ -48,7 +49,7 @@ export const PREDEFINED_SLIDES: CarouselSlide[] = [
undismissable: false,
navigation: {
type: 'function',
navigate: () => [Routes.CONFIRMATION_SWITCH_ACCOUNT_TYPE],
navigate: createSmartAccountNavigationDetails,
},
},
{
Expand Down
5 changes: 5 additions & 0 deletions app/components/UI/Carousel/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ jest.mock('@react-navigation/native', () => ({
jest.mock('../../../core/Engine', () => ({
getTotalEvmFiatAccountBalance: jest.fn(),
setSelectedAddress: jest.fn(),
context: {
PreferencesController: {
state: {},
},
},
}));

jest.mock('../../../util/theme', () => ({
Expand Down
4 changes: 3 additions & 1 deletion app/components/UI/Carousel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ interface NavigationScreen {
params: NavigationParams;
}

type NavigationRoute = readonly [string] | readonly [string, NavigationScreen];
export type NavigationRoute =
| readonly [string]
| readonly [string, NavigationScreen];

export interface UrlNavigationAction {
type: 'url';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jest.mock('../../../../../core/Engine', () => ({
id: '01JNG7170V9X27V5NFDTY04PJ4',
name: '',
},
}
},
],
},
getOrAddQRKeyring: jest.fn(),
Expand Down Expand Up @@ -287,7 +287,6 @@ describe('Confirm', () => {
});

expect(getByText('Use smart account?')).toBeTruthy();
expect(getByText('Request for')).toBeTruthy();
});

it('returns null if confirmation redesign is not enabled', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SmartAccountUpdateContent } from './smart-account-update-content';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { StyleSheet } from 'react-native';

const styleSheet = () =>
StyleSheet.create({
image: {
marginTop: 28,
height: '28%',
width: '100%',
},
title: {
marginLeft: 8,
},
requestSection: {
flexDirection: 'row',
alignItems: 'center',
},
icon: {
height: 24,
width: 24,
},
listWrapper: {
flexDirection: 'row',
alignItems: 'flex-start',
paddingInline: 2,
},
textSection: {
marginLeft: 8,
width: '90%',
},
accountIcon: {
marginRight: -6,
},
});

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';

import renderWithProvider from '../../../../../util/test/renderWithProvider';
import { backgroundState } from '../../../../../util/test/initial-root-state';
import { SmartAccountUpdateContent } from './smart-account-update-content';

const renderComponent = () =>
renderWithProvider(<SmartAccountUpdateContent />, {
state: {
engine: { backgroundState },
},
});

describe('SmartContractWithLogo', () => {
it('renders correctly', () => {
const { getByText } = renderComponent();
expect(getByText('Use smart account?')).toBeTruthy();
expect(getByText('Faster transactions, lower fees')).toBeTruthy();
expect(getByText('Pay with any token, any time')).toBeTruthy();
expect(getByText('Same account, smarter features.')).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React, { ReactElement } from 'react';
import { Image, Linking, View } from 'react-native';

import { strings } from '../../../../../../locales/i18n';
import AppConstants from '../../../../../core/AppConstants';
import AvatarIcon from '../../../../../component-library/components/Avatars/Avatar/variants/AvatarIcon';
import Text, {
TextColor,
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
import { IconName } from '../../../../../component-library/components/Icons/Icon';
import { useTheme } from '../../../../../util/theme';
import { useStyles } from '../../../../hooks/useStyles';
import styleSheet from './smart-account-update-content.styles';

// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires, import/no-commonjs
const smartAccountUpdateImage = require('../../../../../images/smart-account-update.png');

const ListItem = ({
iconName,
title,
description,
styles,
}: {
iconName: IconName;
title: string;
description: ReactElement;
styles: ReturnType<typeof styleSheet>;
}) => {
const { colors } = useTheme();
return (
<View style={styles.listWrapper}>
<AvatarIcon
name={iconName}
iconColor={colors.primary.default}
backgroundColor={colors.primary.muted}
/>
<View style={styles.textSection}>
<Text variant={TextVariant.BodyMDBold}>{title}</Text>
<Text color={TextColor.Alternative} variant={TextVariant.BodyMD}>
{description}
</Text>
</View>
</View>
);
};

export const SmartAccountUpdateContent = () => {
const { styles } = useStyles(styleSheet, {});

return (
<>
<Image source={smartAccountUpdateImage} style={styles.image} />
<Text variant={TextVariant.HeadingLG} style={styles.title}>
{strings('confirm.7702_functionality.splashpage.splashTitle')}
</Text>
<ListItem
iconName={IconName.Speedometer}
title={strings(
'confirm.7702_functionality.splashpage.betterTransaction',
)}
description={strings(
'confirm.7702_functionality.splashpage.betterTransactionDescription',
)}
styles={styles}
/>
<ListItem
iconName={IconName.Gas}
title={strings('confirm.7702_functionality.splashpage.payToken')}
description={strings(
'confirm.7702_functionality.splashpage.payTokenDescription',
)}
styles={styles}
/>
<ListItem
iconName={IconName.Sparkle}
title={strings('confirm.7702_functionality.splashpage.sameAccount')}
description={
<>
<Text color={TextColor.Alternative} variant={TextVariant.BodyMD}>
{strings(
'confirm.7702_functionality.splashpage.featuresDescription',
)}{' '}
<Text
color={TextColor.Primary}
onPress={() =>
Linking.openURL(AppConstants.URLS.SMART_ACCOUNTS)
}
>
{strings('alert_system.upgrade_account.learn_more')}
</Text>
</Text>
</>
}
styles={styles}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SmartAccountUpdateModal } from './smart-account-update-modal';
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Theme } from '@metamask/design-tokens';
import { StyleSheet } from 'react-native';

const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;

return StyleSheet.create({
bottomSheet: {
backgroundColor: theme.colors.background.alternative,
},
wrapper: {
backgroundColor: theme.colors.background.alternative,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'space-between',
height: 640,
width: '100%',
paddingInline: 8,
paddingTop: 20,
},
actionIcon: {
position: 'absolute',
right: 10,
top: 8,
},
button: {
alignSelf: 'center',
marginVertical: 12,
width: '90%',
},
successWrapper: {
backgroundColor: theme.colors.background.alternative,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
height: 640,
width: '100%',
paddingInline: 8,
paddingTop: 20,
},
successInner: {
height: '28%',
width: '80%',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'space-between',
},
});
};

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import { fireEvent } from '@testing-library/react-native';

import Engine from '../../../../../core/Engine';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import {
getAppStateForConfirmation,
upgradeAccountConfirmation,
} from '../../../../../util/test/confirm-data-helpers';
// eslint-disable-next-line import/no-namespace
import * as AddressUtils from '../../../../../util/address';
import { SmartAccountUpdateModal } from './smart-account-update-modal';

jest.mock('react-native-safe-area-context', () => {
// using disting digits for mock rects to make sure they are not mixed up
const inset = { top: 1, right: 2, bottom: 3, left: 4 };
const frame = { width: 5, height: 6, x: 7, y: 8 };
return {
SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children),
SafeAreaConsumer: jest
.fn()
.mockImplementation(({ children }) => children(inset)),
useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
useSafeAreaFrame: jest.fn().mockImplementation(() => frame),
};
});

jest.mock('../../../../hooks/AssetPolling/AssetPollingProvider', () => ({
AssetPollingProvider: () => null,
}));

jest.mock('../../../../../core/Engine', () => ({
getTotalEvmFiatAccountBalance: () => ({ tokenFiat: 10 }),
context: {
PreferencesController: {
setSmartAccountOptIn: jest.fn(),
},
},
}));

jest.mock('@react-navigation/native', () => {
const actualNav = jest.requireActual('@react-navigation/native');
return {
...actualNav,
useNavigation: () => ({
navigate: jest.fn(),
}),
};
});

const renderComponent = (state?: Record<string, unknown>) =>
renderWithProvider(<SmartAccountUpdateModal />, {
state:
state ??
getAppStateForConfirmation(upgradeAccountConfirmation, {
PreferencesController: { smartAccountOptIn: false },
}),
});

describe('SmartAccountUpdateModal', () => {
beforeEach(() => {
jest.spyOn(AddressUtils, 'isHardwareAccount').mockReturnValue(false);
});

it('renders correctly', () => {
const { getByText } = renderComponent();
expect(getByText('Use smart account?')).toBeTruthy();
});

it('show success after `Yes` button is clicked', () => {
const { getByText, queryByText } = renderComponent();
expect(getByText('Use smart account?')).toBeTruthy();
fireEvent.press(getByText('Use smart account'));
expect(
Engine.context.PreferencesController.setSmartAccountOptIn,
).toHaveBeenCalled();
expect(queryByText('Use smart account?')).toBeNull();
expect(getByText('Successful!')).toBeTruthy();
});
});
Loading
Loading