Skip to content

Commit 22f9bd9

Browse files
refactor(ProtectYourWalletModal): migrate to design system components (#27584)
## **Description** Replaces legacy styling and icon primitives in `ProtectYourWalletModal` with design system components, keeping the existing `PureComponent` + `connect()` + `withAnalyticsAwareness` architecture unchanged. **What changed (visual layer only):** - `View` → `Box` from `@metamask/design-system-react-native` - `StyleSheet.create()` + theme-based color styles → `twClassName` Tailwind utilities (DS handles theming internally) - Raw `Text` with `fontStyles` → DS `Text` with `TextVariant`, `TextColor`, `FontWeight` - `FontAwesome` icon + `TouchableOpacity` (close button) → DS `ButtonIcon` - `TouchableOpacity` + `Text` (Learn more) → DS `Button` (`ButtonVariant.Tertiary`) - Remove `ThemeContext` / `mockTheme` imports (no longer needed) - Remove `fontStyles` import (no longer needed) - PNG `require()` + `eslint-disable` comment → clean ES module import ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: <!-- no linked issue --> ## **Manual testing steps** ```gherkin Feature: Protect Your Wallet Modal — visual regression check Scenario: Modal renders correctly (light + dark mode) Given the user has a wallet without a backed-up seed phrase When the wallet home screen loads Then the Protect Your Wallet modal appears And the title, lock image, body text, close button, and Learn more button are visible And the layout matches the before/after screenshots in both light and dark mode Scenario: All interactions still work Given the modal is visible When user taps the X close button — modal dismisses When user taps "Protect wallet" — navigates to backup SRP flow When user taps "Learn more" — opens MetaMask safety tips webview When user taps "Remind me later" — modal dismisses ``` | | Before | After | |-------------|--------|-------| | **Light** | <img src="https://github.com/user-attachments/assets/58fded8f-b1e3-46c7-a2ab-a7d873268c23" width="300"/> | <img src="https://github.com/user-attachments/assets/d3c4ee2f-8806-4803-ab6d-2b71bdc87b3c" width="300"/> | | **Dark** | <img src="https://github.com/user-attachments/assets/ff9d2404-6b35-487a-85c6-81eeafe6af9f" width="300"/> | <img src="https://github.com/user-attachments/assets/342f01a9-a860-49f9-ae8f-5404946dff3a" width="300"/> | ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk UI refactor that replaces legacy styling/components with design-system `Box`/`Text`/`Button` variants while keeping the same navigation and analytics flows. > > **Overview** > Refactors `ProtectYourWalletModal` to use MetaMask design-system components and Tailwind-style `twClassName` layout instead of legacy `StyleSheet` + `View`/`TouchableOpacity` + FontAwesome close icon. > > The modal’s structure is preserved (same cancel/confirm handlers, navigation targets, analytics events, and seedless-onboarding suppression), but the header close control is now a DS `ButtonIcon`, the “Learn more” action is a DS `Button` (`Tertiary`), and the image is imported via ES module rather than `require()`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 8571195. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 5cb34da commit 22f9bd9

1 file changed

Lines changed: 63 additions & 111 deletions

File tree

  • app/components/UI/ProtectYourWalletModal

app/components/UI/ProtectYourWalletModal/index.js

Lines changed: 63 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,42 @@
11
import React, { PureComponent } from 'react';
22
import PropTypes from 'prop-types';
3-
import { StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native';
3+
import { Image } from 'react-native';
4+
import {
5+
Box,
6+
Text,
7+
TextVariant,
8+
TextColor,
9+
FontWeight,
10+
BoxFlexDirection,
11+
BoxAlignItems,
12+
BoxJustifyContent,
13+
IconName,
14+
IconSize,
15+
IconColor,
16+
Button,
17+
ButtonVariant,
18+
ButtonIcon,
19+
ButtonIconSize,
20+
} from '@metamask/design-system-react-native';
421
import ActionModal from '../ActionModal';
5-
import { fontStyles } from '../../../styles/common';
622
import { connect } from 'react-redux';
723
import { protectWalletModalNotVisible } from '../../../actions/user';
8-
import Icon from 'react-native-vector-icons/FontAwesome';
924
import { strings } from '../../../../locales/i18n';
1025
import scaling from '../../../util/scaling';
1126
import { MetaMetricsEvents } from '../../../core/Analytics/MetaMetrics.events';
12-
13-
import { ThemeContext, mockTheme } from '../../../util/theme';
1427
import { ProtectWalletModalSelectorsIDs } from './ProtectWalletModal.testIds';
1528
import { withAnalyticsAwareness } from '../../../components/hooks/useAnalytics/withAnalyticsAwareness';
1629
import { selectSeedlessOnboardingLoginFlow } from '../../../selectors/seedlessOnboardingController';
1730

18-
const protectWalletImage = require('../../../images/explain-backup-seedphrase.png'); // eslint-disable-line
19-
20-
const createStyles = (colors) =>
21-
StyleSheet.create({
22-
wrapper: {
23-
marginTop: 24,
24-
marginHorizontal: 24,
25-
flex: 1,
26-
},
27-
title: {
28-
...fontStyles.bold,
29-
color: colors.text.default,
30-
textAlign: 'center',
31-
fontSize: 20,
32-
flex: 1,
33-
},
34-
imageWrapper: {
35-
flexDirection: 'column',
36-
alignItems: 'center',
37-
marginBottom: 12,
38-
marginTop: 30,
39-
},
40-
image: {
41-
width: scaling.scale(135, { baseModel: 1 }),
42-
height: scaling.scale(160, { baseModel: 1 }),
43-
},
44-
text: {
45-
...fontStyles.normal,
46-
color: colors.text.default,
47-
textAlign: 'center',
48-
fontSize: 14,
49-
marginBottom: 24,
50-
},
51-
closeIcon: {
52-
padding: 5,
53-
},
54-
learnMoreText: {
55-
textAlign: 'center',
56-
...fontStyles.normal,
57-
color: colors.primary.default,
58-
marginBottom: 14,
59-
fontSize: 14,
60-
},
61-
modalXIcon: {
62-
fontSize: 16,
63-
color: colors.text.default,
64-
},
65-
titleWrapper: {
66-
flexDirection: 'row',
67-
justifyContent: 'center',
68-
alignItems: 'center',
69-
},
70-
auxCenter: {
71-
width: 26,
72-
},
73-
});
31+
import protectWalletImage from '../../../images/explain-backup-seedphrase.png';
7432

75-
/**
76-
* View that renders an action modal
77-
*/
7833
class ProtectYourWalletModal extends PureComponent {
7934
static propTypes = {
8035
navigation: PropTypes.object,
81-
/**
82-
* Hide this modal
83-
*/
8436
protectWalletModalNotVisible: PropTypes.func,
85-
/**
86-
* Whether this modal is visible
87-
*/
8837
protectWalletModalVisible: PropTypes.bool,
89-
/**
90-
* Boolean that determines if the user has set a password before
91-
*/
9238
passwordSet: PropTypes.bool,
93-
/**
94-
* Analytics injected by withAnalyticsAwareness HOC
95-
*/
9639
analytics: PropTypes.object,
97-
/**
98-
* A boolean representing if the user is in the seedless onboarding login flow
99-
*/
10040
isSeedlessOnboardingLoginFlow: PropTypes.bool,
10141
};
10242

@@ -142,10 +82,6 @@ class ProtectYourWalletModal extends PureComponent {
14282
};
14383

14484
render() {
145-
const colors = this.context.colors || mockTheme.colors;
146-
const styles = createStyles(colors);
147-
148-
// will not render if the user is in the seedless onboarding login flow
14985
if (this.props.isSeedlessOnboardingLoginFlow) {
15086
return null;
15187
}
@@ -164,43 +100,62 @@ class ProtectYourWalletModal extends PureComponent {
164100
cancelTestID={ProtectWalletModalSelectorsIDs.CANCEL_BUTTON}
165101
confirmTestID={ProtectWalletModalSelectorsIDs.CONFIRM_BUTTON}
166102
>
167-
<View
168-
style={styles.wrapper}
103+
<Box
104+
twClassName="mt-6 mx-6 flex-1"
169105
testID={ProtectWalletModalSelectorsIDs.CONTAINER}
170106
>
171-
<View style={styles.titleWrapper}>
172-
<View style={styles.auxCenter} />
173-
<Text style={styles.title}>
107+
<Box
108+
flexDirection={BoxFlexDirection.Row}
109+
justifyContent={BoxJustifyContent.Center}
110+
alignItems={BoxAlignItems.Center}
111+
>
112+
<Box twClassName="w-6" />
113+
<Text
114+
variant={TextVariant.HeadingMd}
115+
fontWeight={FontWeight.Bold}
116+
color={TextColor.TextDefault}
117+
twClassName="text-center flex-1"
118+
>
174119
{strings('protect_wallet_modal.title')}
175120
</Text>
176-
<TouchableOpacity
121+
<ButtonIcon
122+
iconName={IconName.Close}
123+
size={ButtonIconSize.Sm}
124+
iconProps={{ color: IconColor.IconDefault, size: IconSize.Sm }}
177125
onPress={this.onDismiss}
178-
style={styles.closeIcon}
179-
hitSlop={{ top: 10, left: 10, bottom: 10, right: 10 }}
180-
>
181-
<Icon name="times" style={styles.modalXIcon} />
182-
</TouchableOpacity>
183-
</View>
184-
<View style={styles.imageWrapper}>
185-
<Image source={protectWalletImage} style={styles.image} />
186-
</View>
187-
188-
<Text style={styles.text}>
126+
/>
127+
</Box>
128+
129+
<Box alignItems={BoxAlignItems.Center} twClassName="mb-3 mt-8">
130+
<Image
131+
source={protectWalletImage}
132+
style={{
133+
width: scaling.scale(135, { baseModel: 1 }),
134+
height: scaling.scale(160, { baseModel: 1 }),
135+
}}
136+
/>
137+
</Box>
138+
139+
<Text
140+
variant={TextVariant.BodySm}
141+
color={TextColor.TextDefault}
142+
twClassName="text-center mb-6"
143+
>
189144
{strings('protect_wallet_modal.text')}
190-
<Text style={{ ...fontStyles.bold }}>
145+
<Text variant={TextVariant.BodySm} fontWeight={FontWeight.Bold}>
191146
{' ' + strings('protect_wallet_modal.text_bold')}
192147
</Text>
193148
</Text>
194149

195-
<TouchableOpacity
150+
<Button
151+
variant={ButtonVariant.Tertiary}
196152
onPress={this.onLearnMore}
197153
testID={ProtectWalletModalSelectorsIDs.LEARN_MORE_BUTTON}
154+
twClassName="w-full mb-3.5"
198155
>
199-
<Text style={styles.learnMoreText}>
200-
{strings('protect_wallet_modal.action')}
201-
</Text>
202-
</TouchableOpacity>
203-
</View>
156+
{strings('protect_wallet_modal.action')}
157+
</Button>
158+
</Box>
204159
</ActionModal>
205160
);
206161
}
@@ -213,12 +168,9 @@ const mapStateToProps = (state) => ({
213168
});
214169

215170
const mapDispatchToProps = (dispatch) => ({
216-
protectWalletModalNotVisible: (enable) =>
217-
dispatch(protectWalletModalNotVisible()),
171+
protectWalletModalNotVisible: () => dispatch(protectWalletModalNotVisible()),
218172
});
219173

220-
ProtectYourWalletModal.contextType = ThemeContext;
221-
222174
export default connect(
223175
mapStateToProps,
224176
mapDispatchToProps,

0 commit comments

Comments
 (0)