Skip to content

Commit b3aa984

Browse files
committed
Add onboarding steps
1 parent aa650f2 commit b3aa984

File tree

14 files changed

+1016
-19
lines changed

14 files changed

+1016
-19
lines changed

app/_locales/en/messages.json

Lines changed: 54 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

types/global.d.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,30 @@ export declare global {
360360
}
361361
// #endregion
362362

363+
// #region Global Engine
364+
// Minimal ambient declaration for the UI's global `Engine` usage.
365+
// Provides a lightweight type for `controllerMessenger.call(...)` so
366+
// TypeScript can resolve `Engine` without explicit imports in UI code.
367+
export declare global {
368+
type GlobalControllerMessenger = {
369+
// Accepts string-based action names like 'RewardsController:validateReferralCode'
370+
// and any additional arguments.
371+
// Return type is intentionally broad to avoid over-constraining UI usage.
372+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
373+
call: (type: string, ...args: any[]) => any;
374+
// Optional helpers commonly present on messengers
375+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
376+
subscribe?: (...args: any[]) => void;
377+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
378+
publish?: (...args: any[]) => void;
379+
};
380+
381+
var Engine: {
382+
controllerMessenger: GlobalControllerMessenger;
383+
};
384+
}
385+
// #endregion
386+
363387
// #region used in jest tests to ignore unhandled rejections
364388
declare global {
365389
namespace NodeJS {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React from 'react';
2+
import {
3+
Box,
4+
Text,
5+
Button,
6+
Icon,
7+
TextVariant,
8+
BoxFlexDirection,
9+
BoxAlignItems,
10+
ButtonVariant,
11+
ButtonSize,
12+
IconName,
13+
IconSize,
14+
FontWeight,
15+
} from '@metamask/design-system-react';
16+
17+
interface RewardsErrorBannerProps {
18+
title: string;
19+
description: string;
20+
onDismiss?: () => void;
21+
onConfirm?: () => void;
22+
confirmButtonLabel?: string;
23+
onConfirmLoading?: boolean;
24+
testID?: string;
25+
}
26+
27+
const RewardsErrorBanner: React.FC<RewardsErrorBannerProps> = ({
28+
title,
29+
description,
30+
onDismiss,
31+
onConfirm,
32+
confirmButtonLabel,
33+
onConfirmLoading,
34+
testID,
35+
}) => (
36+
<Box
37+
flexDirection={BoxFlexDirection.Row}
38+
alignItems={BoxAlignItems.Start}
39+
className="bg-error-muted rounded-2xl p-4 gap-4 w-full"
40+
data-testid={testID}
41+
>
42+
{/* Column 1: Error Icon */}
43+
<Icon
44+
name={IconName.Error}
45+
size={IconSize.Xl}
46+
className="text-error-default"
47+
/>
48+
49+
{/* Column 2: Content */}
50+
<Box className="flex-1 gap-4">
51+
<Box className="gap-1">
52+
{/* Title */}
53+
<Text variant={TextVariant.BodyMd} fontWeight={FontWeight.Bold}>
54+
{title}
55+
</Text>
56+
57+
{/* Description */}
58+
<Text variant={TextVariant.BodyMd}>{description}</Text>
59+
</Box>
60+
61+
{/* Button Section */}
62+
{(onDismiss || onConfirm) && (
63+
<Box flexDirection={BoxFlexDirection.Row} className="gap-3">
64+
{onDismiss && (
65+
<Button
66+
variant={ButtonVariant.Secondary}
67+
size={ButtonSize.Md}
68+
onClick={onDismiss}
69+
>
70+
Dismiss
71+
</Button>
72+
)}
73+
{onConfirm && (
74+
<Button
75+
variant={ButtonVariant.Primary}
76+
size={ButtonSize.Md}
77+
onClick={onConfirm}
78+
isLoading={onConfirmLoading}
79+
>
80+
{confirmButtonLabel || 'Confirm'}
81+
</Button>
82+
)}
83+
</Box>
84+
)}
85+
</Box>
86+
</Box>
87+
);
88+
89+
export default RewardsErrorBanner;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const REWARDS_ONBOARD_OPTIN_LEGAL_LEARN_MORE_URL =
2+
'https://support.metamask.io/manage-crypto/metamask-rewards';
3+
4+
export const REWARDS_ONBOARD_TERMS_URL = 'https://go.metamask.io/rewards-terms';

ui/components/app/rewards/onboarding/onboarding-intro-step.tsx renamed to ui/components/app/rewards/components/onboarding/onboarding-intro-step.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import {
1010
Text,
1111
TextVariant,
1212
} from '@metamask/design-system-react';
13-
import { OnboardingStep } from '../../../../ducks/rewards/types';
13+
import { OnboardingStep } from '../../../../../ducks/rewards/types';
1414
import {
1515
setOnboardingActiveStep,
1616
setOnboardingModalOpen,
17-
} from '../../../../ducks/rewards';
18-
import { ModalBody } from '../../../component-library';
19-
import { useI18nContext } from '../../../../hooks/useI18nContext';
17+
} from '../../../../../ducks/rewards';
18+
import { ModalBody } from '../../../../component-library';
19+
import { useI18nContext } from '../../../../../hooks/useI18nContext';
2020

2121
/**
2222
* OnboardingIntroStep Component

ui/components/app/rewards/onboarding/onboarding-modal.tsx renamed to ui/components/app/rewards/components/onboarding/onboarding-modal.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@ import {
66
ModalHeader,
77
ModalOverlay,
88
ModalContentSize,
9-
} from '../../../component-library';
10-
import { ThemeType } from '../../../../../shared/constants/preferences';
9+
} from '../../../../component-library';
10+
import { ThemeType } from '../../../../../../shared/constants/preferences';
1111
import {
1212
AlignItems,
1313
JustifyContent,
14-
} from '../../../../helpers/constants/design-system';
14+
} from '../../../../../helpers/constants/design-system';
1515
import {
1616
selectOnboardingModalOpen,
1717
selectOnboardingActiveStep,
18-
} from '../../../../ducks/rewards/selectors';
19-
import { setOnboardingModalOpen } from '../../../../ducks/rewards';
20-
import { OnboardingStep } from '../../../../ducks/rewards/types';
18+
} from '../../../../../ducks/rewards/selectors';
19+
import { setOnboardingModalOpen } from '../../../../../ducks/rewards';
20+
import { OnboardingStep } from '../../../../../ducks/rewards/types';
2121
import OnboardingIntroStep from './onboarding-intro-step';
2222
import OnboardingStep1 from './onboarding-step-1';
23+
import OnboardingStep2 from './onboarding-step-2';
24+
import OnboardingStep3 from './onboarding-step-3';
25+
import OnboardingStep4 from './onboarding-step-4';
2326

2427
// eslint-disable-next-line @typescript-eslint/naming-convention
2528
export default function OnboardingModal() {
@@ -37,6 +40,12 @@ export default function OnboardingModal() {
3740
return <OnboardingIntroStep />;
3841
case OnboardingStep.STEP_1:
3942
return <OnboardingStep1 />;
43+
case OnboardingStep.STEP_2:
44+
return <OnboardingStep2 />;
45+
case OnboardingStep.STEP_3:
46+
return <OnboardingStep3 />;
47+
case OnboardingStep.STEP_4:
48+
return <OnboardingStep4 />;
4049
default:
4150
return <OnboardingIntroStep />;
4251
}

ui/components/app/rewards/onboarding/onboarding-step-1.tsx renamed to ui/components/app/rewards/components/onboarding/onboarding-step-1.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { useDispatch } from 'react-redux';
22
import React, { useCallback } from 'react';
3-
import { setOnboardingActiveStep } from '../../../../ducks/rewards';
4-
import { OnboardingStep } from '../../../../ducks/rewards/types';
3+
import { setOnboardingActiveStep } from '../../../../../ducks/rewards';
4+
import { OnboardingStep } from '../../../../../ducks/rewards/types';
55
import {
66
Box,
77
Button,
88
ButtonSize,
99
Text,
1010
TextVariant,
1111
} from '@metamask/design-system-react';
12-
import { ModalBody } from '../../../component-library';
13-
import { useI18nContext } from '../../../../hooks/useI18nContext';
12+
import { ModalBody } from '../../../../component-library';
13+
import { useI18nContext } from '../../../../../hooks/useI18nContext';
1414

1515
const OnboardingStep1: React.FC = () => {
1616
const dispatch = useDispatch();
@@ -45,13 +45,13 @@ const OnboardingStep1: React.FC = () => {
4545
);
4646

4747
const renderStepInfo = () => (
48-
<Box className="flex-col min-h-30">
49-
<Text variant={TextVariant.HeadingLg} className="text-center my-2">
48+
<Box className="flex flex-col min-h-30 gap-2">
49+
<Text variant={TextVariant.HeadingLg} className="text-center">
5050
{t('rewardsOnboardingStep1Title')}
5151
</Text>
5252
<Text
5353
variant={TextVariant.BodyMd}
54-
className="text-center text-alternative my-2"
54+
className="text-center text-alternative"
5555
>
5656
{t('rewardsOnboardingStep1Description')}
5757
</Text>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useDispatch } from 'react-redux';
2+
import React, { useCallback } from 'react';
3+
import { setOnboardingActiveStep } from '../../../../../ducks/rewards';
4+
import { OnboardingStep } from '../../../../../ducks/rewards/types';
5+
import {
6+
Box,
7+
Button,
8+
ButtonSize,
9+
Text,
10+
TextVariant,
11+
} from '@metamask/design-system-react';
12+
import { ModalBody } from '../../../../component-library';
13+
import { useI18nContext } from '../../../../../hooks/useI18nContext';
14+
15+
const OnboardingStep2: React.FC = () => {
16+
const dispatch = useDispatch();
17+
18+
const t = useI18nContext();
19+
20+
const handleNext = useCallback(() => {
21+
dispatch(setOnboardingActiveStep(OnboardingStep.STEP_3));
22+
}, [dispatch]);
23+
24+
const renderStepImage = () => (
25+
<>
26+
<svg
27+
className="w-full absolute"
28+
viewBox="0 0 393 454"
29+
xmlns="http://www.w3.org/2000/svg"
30+
aria-hidden="true"
31+
focusable="false"
32+
style={{ left: 0 }}
33+
>
34+
<path
35+
d="M 257.61523 0 L 121.50195 136.11328 L 121.50195 272.51562 L 30.742188 272.51562 L 30.742188 363.25781 L 166.87109 363.25781 L 166.87109 454 L 302.98438 454 L 393.72852 363.25781 L 393.71289 363.25781 L 393.71289 272.51562 L 393.73047 272.51562 L 302.79492 227.04688 L 393.73047 136.11328 L 393.73047 0 L 257.61523 0 z M 30.742188 363.25781 L -60 363.25781 L -60 454 L 30.742188 454 L 30.742188 363.25781 z M -59.998047 45.660156 L -14.626953 136.40234 L 121.48633 136.40234 L 76.115234 45.660156 L -59.998047 45.660156 z M 121.48633 181.76953 L 30.744141 272.51367 L 121.48633 272.51367 L 121.48633 181.76953 z M 212.24414 272.36914 L 212.24414 272.51562 L 165.20898 272.51562 L 212.24414 272.36914 z "
36+
fill="var(--color-background-muted)"
37+
/>
38+
</svg>
39+
40+
<img
41+
src="/images/rewards/rewards-onboarding-step2.png"
42+
className="w-full z-10 object-contain"
43+
/>
44+
</>
45+
);
46+
47+
const renderStepInfo = () => (
48+
<Box className="flex flex-col min-h-30 gap-2">
49+
<Text variant={TextVariant.HeadingLg} className="text-center">
50+
{t('rewardsOnboardingStep2Title')}
51+
</Text>
52+
<Text
53+
variant={TextVariant.BodyMd}
54+
className="text-center text-alternative"
55+
>
56+
{t('rewardsOnboardingStep2Description')}
57+
</Text>
58+
</Box>
59+
);
60+
61+
/**
62+
* Renders the action buttons section
63+
*/
64+
const renderActions = () => (
65+
<Box className="flex flex-col justify-end flex-1 mb-2">
66+
<Button
67+
size={ButtonSize.Lg}
68+
onClick={handleNext}
69+
className="w-full bg-white my-2"
70+
>
71+
{t('rewardsOnboardingStepConfirm')}
72+
</Button>
73+
</Box>
74+
);
75+
76+
return (
77+
<ModalBody className="w-full h-full pt-8 pb-4 flex flex-col">
78+
{/* Image Section */}
79+
{renderStepImage()}
80+
81+
{/* Title Section */}
82+
{renderStepInfo()}
83+
84+
{/* Actions Section */}
85+
{renderActions()}
86+
</ModalBody>
87+
);
88+
};
89+
90+
export default OnboardingStep2;

0 commit comments

Comments
 (0)