Skip to content

Commit ae93e56

Browse files
committed
feat(money-account): add real activity to the money account
1 parent c99e886 commit ae93e56

22 files changed

Lines changed: 890 additions & 111 deletions

app/components/UI/Money/Views/MoneyActivityView/MoneyActivityView.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ describe('MoneyActivityView', () => {
7474
transfers: MOCK_TRANSFERS,
7575
submittedTransactions: [],
7676
moneyAddress: '0x0000000000000000000000000000000000000001',
77+
mockDataEnabled: false,
7778
});
7879
});
7980

@@ -144,6 +145,7 @@ describe('MoneyActivityView', () => {
144145
transfers: [],
145146
submittedTransactions: [],
146147
moneyAddress: '0x0000000000000000000000000000000000000001',
148+
mockDataEnabled: false,
147149
});
148150

149151
const { getByTestId } = renderWithProvider(<MoneyActivityView />);

app/components/UI/Money/Views/MoneyActivityView/MoneyActivityView.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import MoneyActivityItem from '../../components/MoneyActivityItem';
2525
import { useMoneyAccountTransactions } from '../../hooks/useMoneyAccountTransactions';
2626
import { getMoneyActivityDateKeyUtc } from '../../constants/moneyActivityFilters';
2727
import { MoneyActivityFilter } from '../../constants/mockActivityData';
28-
import { showMoneyActivityUnderConstructionAlert } from '../../constants/showMoneyActivityUnderConstructionAlert';
28+
import Routes from '../../../../../constants/navigation/Routes';
2929
import { MoneyActivityViewTestIds } from './MoneyActivityView.testIds';
3030

3131
const styles = StyleSheet.create({
@@ -67,16 +67,22 @@ const MoneyActivityView = () => {
6767
const { colors } = useTheme();
6868
const [filter, setFilter] = useState(MoneyActivityFilter.All);
6969

70-
const { allTransactions, deposits, transfers, moneyAddress } =
70+
const { allTransactions, deposits, transfers, moneyAddress, mockDataEnabled } =
7171
useMoneyAccountTransactions();
7272

7373
const handleBackPress = useCallback(() => {
7474
navigation.goBack();
7575
}, [navigation]);
7676

77-
const handleItemPress = useCallback(() => {
78-
showMoneyActivityUnderConstructionAlert();
79-
}, []);
77+
const handleItemPress = useCallback(
78+
(transactionId: string) => {
79+
navigation.navigate(Routes.MONEY.MODALS.ROOT, {
80+
screen: Routes.MONEY.MODALS.TRANSACTION_DETAILS_SHEET,
81+
params: { transactionId },
82+
});
83+
},
84+
[navigation],
85+
);
8086

8187
const filtered = useMemo(() => {
8288
if (filter === MoneyActivityFilter.All) {
@@ -110,7 +116,7 @@ const MoneyActivityView = () => {
110116
<MoneyActivityItem
111117
tx={item}
112118
moneyAddress={moneyAddress}
113-
onPress={handleItemPress}
119+
onPress={mockDataEnabled ? undefined : handleItemPress}
114120
/>
115121
);
116122

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ describe('MoneyHomeView', () => {
236236
transfers: [],
237237
submittedTransactions: [],
238238
moneyAddress: '0x0000000000000000000000000000000000000001',
239+
mockDataEnabled: false,
239240
});
240241
});
241242

@@ -432,6 +433,7 @@ describe('MoneyHomeView', () => {
432433
transfers: [],
433434
submittedTransactions: [],
434435
moneyAddress: '0x0000000000000000000000000000000000000001',
436+
mockDataEnabled: false,
435437
});
436438

437439
const { getByTestId } = renderWithProvider(<MoneyHomeView />);
@@ -530,6 +532,7 @@ describe('MoneyHomeView', () => {
530532
transfers: [],
531533
submittedTransactions: [],
532534
moneyAddress: '0x0000000000000000000000000000000000000001',
535+
mockDataEnabled: false,
533536
});
534537
});
535538

@@ -591,6 +594,7 @@ describe('MoneyHomeView', () => {
591594
transfers: [],
592595
submittedTransactions: [],
593596
moneyAddress: '0x0000000000000000000000000000000000000001',
597+
mockDataEnabled: false,
594598
});
595599
mockSelectIsCardholder.mockReturnValue(true);
596600
// Non-US so MetaMask card renders in link mode (manage mode is US-only).
@@ -774,6 +778,7 @@ describe('MoneyHomeView', () => {
774778
transfers: [],
775779
submittedTransactions: [],
776780
moneyAddress: '0x0000000000000000000000000000000000000001',
781+
mockDataEnabled: false,
777782
});
778783
});
779784

@@ -996,6 +1001,7 @@ describe('MoneyHomeView', () => {
9961001
transfers: [],
9971002
submittedTransactions: [],
9981003
moneyAddress: '0x0000000000000000000000000000000000000001',
1004+
mockDataEnabled: false,
9991005
});
10001006
});
10011007

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import styleSheet from './MoneyHomeView.styles';
2626
import { useMusdConversionTokens } from '../../../Earn/hooks/useMusdConversionTokens';
2727
import { useMusdConversion } from '../../../Earn/hooks/useMusdConversion';
2828
import { useMoneyAccountTransactions } from '../../hooks/useMoneyAccountTransactions';
29-
import { showMoneyActivityUnderConstructionAlert } from '../../constants/showMoneyActivityUnderConstructionAlert';
3029
import useMoneyAccountBalance from '../../hooks/useMoneyAccountBalance';
3130
import { selectCurrentCurrency } from '../../../../../selectors/currencyRateController';
3231
import { moneyFormatFiat } from '../../utils/moneyFormatFiat';
@@ -68,7 +67,7 @@ const MoneyHomeView = () => {
6867

6968
const { tokens: conversionTokens } = useMusdConversionTokens();
7069
const { initiateCustomConversion } = useMusdConversion();
71-
const { allTransactions, moneyAddress } = useMoneyAccountTransactions();
70+
const { allTransactions, moneyAddress, mockDataEnabled } = useMoneyAccountTransactions();
7271

7372
const isCardholder = useSelector(selectIsCardholder);
7473
const { canLink, openLinkCardSheet } = useMoneyAccountCardLinkage();
@@ -206,9 +205,15 @@ const MoneyHomeView = () => {
206205
}, [navigation]);
207206
const handleActivityHeaderPress = handleViewAllActivityPress;
208207

209-
const handleActivityItemPress = useCallback(() => {
210-
showMoneyActivityUnderConstructionAlert();
211-
}, []);
208+
const handleActivityItemPress = useCallback(
209+
(transactionId: string) => {
210+
navigation.navigate(Routes.MONEY.MODALS.ROOT, {
211+
screen: Routes.MONEY.MODALS.TRANSACTION_DETAILS_SHEET,
212+
params: { transactionId },
213+
});
214+
},
215+
[navigation],
216+
);
212217

213218
const handleOnboardingCtaPress = useCallback(() => {
214219
if (isCardholderWithMilestone) {
@@ -305,7 +310,7 @@ const MoneyHomeView = () => {
305310
: handleViewAllActivityPress
306311
}
307312
onHeaderPress={handleActivityHeaderPress}
308-
onItemPress={handleActivityItemPress}
313+
onItemPress={mockDataEnabled ? undefined : handleActivityItemPress}
309314
/>
310315
<Divider />
311316
</>

app/components/UI/Money/components/MoneyActivityItem/MoneyActivityItem.test.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import { fireEvent } from '@testing-library/react-native';
33
import {
44
type TransactionMeta,
5+
TransactionStatus,
56
TransactionType,
67
} from '@metamask/transaction-controller';
78
import type { Hex } from '@metamask/utils';
@@ -83,6 +84,9 @@ describe('MoneyActivityItem', () => {
8384
primaryAmount: '+$0.00',
8485
fiatAmount: '$0.00',
8586
isIncoming: true,
87+
sourceTokenSymbol: undefined,
88+
sourceTokenImage: undefined,
89+
sourceTokenChainId: undefined,
8690
});
8791
});
8892

@@ -107,6 +111,9 @@ describe('MoneyActivityItem', () => {
107111
primaryAmount: '+$0.00',
108112
fiatAmount: '$0.00',
109113
isIncoming: false,
114+
sourceTokenSymbol: undefined,
115+
sourceTokenImage: undefined,
116+
sourceTokenChainId: undefined,
110117
});
111118

112119
const { queryByText } = renderWithProvider(
@@ -127,6 +134,21 @@ describe('MoneyActivityItem', () => {
127134
expect(onPress).toHaveBeenCalledTimes(1);
128135
});
129136

137+
it('shows "Failed" in the description slot for a failed transaction', () => {
138+
const failedTx = {
139+
...baseTx,
140+
status: TransactionStatus.failed,
141+
} as unknown as TransactionMeta;
142+
143+
const { getByText, queryByText } = renderWithProvider(
144+
<MoneyActivityItem tx={failedTx} moneyAddress="0x1" />,
145+
);
146+
147+
expect(getByText('Failed')).toBeOnTheScreen();
148+
// Normal description should not appear for failed rows
149+
expect(queryByText('Description')).toBeNull();
150+
});
151+
130152
it('renders network badge subtree when showNetworkBadge is true', () => {
131153
const { getByTestId } = renderWithProvider(
132154
<MoneyActivityItem tx={baseTx} moneyAddress="0x1" showNetworkBadge />,

app/components/UI/Money/components/MoneyActivityItem/MoneyActivityItem.tsx

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo } from 'react';
1+
import React from 'react';
22
import { Pressable } from 'react-native';
33
import {
44
Box,
@@ -9,7 +9,11 @@ import {
99
TextVariant,
1010
} from '@metamask/design-system-react-native';
1111
import { useTailwind } from '@metamask/design-system-twrnc-preset';
12-
import type { TransactionMeta } from '@metamask/transaction-controller';
12+
import {
13+
type TransactionMeta,
14+
TransactionStatus,
15+
} from '@metamask/transaction-controller';
16+
import { strings } from '../../../../../../locales/i18n';
1317
import { getNetworkImageSource } from '../../../../../util/networks';
1418
import AvatarToken from '../../../../../component-library/components/Avatars/Avatar/variants/AvatarToken';
1519
import { AvatarSize } from '../../../../../component-library/components/Avatars/Avatar';
@@ -28,7 +32,7 @@ import { MoneyActivityItemTestIds } from './MoneyActivityItem.testIds';
2832
export interface MoneyActivityItemProps {
2933
tx: TransactionMeta;
3034
moneyAddress: string | undefined;
31-
onPress?: () => void;
35+
onPress?: (transactionId: string) => void;
3236
/** When true, shows the chain network badge on the token avatar. Defaults to false. */
3337
showNetworkBadge?: boolean;
3438
}
@@ -43,22 +47,29 @@ const MoneyActivityItem = ({
4347

4448
const display = useMoneyTransactionDisplayInfo(tx, moneyAddress);
4549

46-
const networkImageSource = useMemo(
47-
() =>
48-
showNetworkBadge
49-
? getNetworkImageSource({ chainId: tx.chainId })
50-
: undefined,
51-
[tx.chainId, showNetworkBadge],
52-
);
50+
const networkImageSource = showNetworkBadge
51+
? getNetworkImageSource({ chainId: tx.chainId })
52+
: undefined;
53+
54+
// use the token's own image URI, or the source chain's network icon, or the mUSD icon
55+
const tokenAvatarImageSource = display.sourceTokenImage
56+
? { uri: display.sourceTokenImage }
57+
: display.sourceTokenChainId
58+
? getNetworkImageSource({ chainId: display.sourceTokenChainId })
59+
: MUSD_TOKEN.imageSource;
5360

54-
const amountColor = display.isIncoming
61+
const isFailed = tx.status === TransactionStatus.failed;
62+
63+
const amountColor = isFailed
64+
? TextColor.TextAlternative
65+
: display.isIncoming
5566
? TextColor.SuccessDefault
5667
: TextColor.TextDefault;
5768

5869
return (
5970
<Pressable
6071
accessibilityRole="button"
61-
onPress={onPress}
72+
onPress={onPress ? () => onPress(tx.id) : undefined}
6273
testID={`${MoneyActivityItemTestIds.ROW}-${tx.id}`}
6374
style={({ pressed }) =>
6475
tw.style(
@@ -81,16 +92,16 @@ const MoneyActivityItem = ({
8192
}
8293
>
8394
<AvatarToken
84-
name={MUSD_TOKEN.name}
85-
imageSource={MUSD_TOKEN.imageSource}
95+
name={display.sourceTokenSymbol ?? MUSD_TOKEN.name}
96+
imageSource={tokenAvatarImageSource}
8697
size={AvatarSize.Lg}
8798
/>
8899
</BadgeWrapper>
89100
) : (
90101
<Box twClassName="self-center">
91102
<AvatarToken
92-
name={MUSD_TOKEN.name}
93-
imageSource={MUSD_TOKEN.imageSource}
103+
name={display.sourceTokenSymbol ?? MUSD_TOKEN.name}
104+
imageSource={tokenAvatarImageSource}
94105
size={AvatarSize.Lg}
95106
/>
96107
</Box>
@@ -104,7 +115,16 @@ const MoneyActivityItem = ({
104115
>
105116
{display.label}
106117
</Text>
107-
{display.description ? (
118+
{isFailed ? (
119+
<Text
120+
variant={TextVariant.BodySm}
121+
fontWeight={FontWeight.Medium}
122+
color={TextColor.ErrorDefault}
123+
numberOfLines={1}
124+
>
125+
{strings('money.transaction.failed')}
126+
</Text>
127+
) : display.description ? (
108128
<Text
109129
variant={TextVariant.BodySm}
110130
fontWeight={FontWeight.Medium}

app/components/UI/Money/components/MoneyActivityList/MoneyActivityList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface MoneyActivityListProps {
1717
moneyAddress?: string;
1818
onViewAllPress?: () => void;
1919
onHeaderPress?: () => void;
20-
onItemPress?: () => void;
20+
onItemPress?: (transactionId: string) => void;
2121
}
2222

2323
const MoneyActivityList = ({
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React, { useCallback, useRef } from 'react';
2+
import { useNavigation } from '@react-navigation/native';
3+
import {
4+
BottomSheet,
5+
type BottomSheetRef,
6+
BottomSheetHeader,
7+
Text,
8+
TextVariant,
9+
} from '@metamask/design-system-react-native';
10+
import {
11+
type TransactionMeta,
12+
TransactionType,
13+
} from '@metamask/transaction-controller';
14+
import { strings } from '../../../../../../locales/i18n';
15+
import { TransactionDetails } from '../../../../Views/confirmations/components/activity/transaction-details/transaction-details';
16+
import { useTransactionDetails } from '../../../../Views/confirmations/hooks/activity/useTransactionDetails';
17+
18+
const TITLE_KEYS: Partial<Record<TransactionType, string>> = {
19+
[TransactionType.moneyAccountDeposit]:
20+
'transaction_details.title.money_account_deposit',
21+
[TransactionType.moneyAccountWithdraw]:
22+
'transaction_details.title.money_account_withdraw',
23+
[TransactionType.musdConversion]: 'transaction_details.title.musd_conversion',
24+
[TransactionType.musdClaim]: 'transaction_details.title.musd_claim',
25+
[TransactionType.perpsDeposit]: 'transaction_details.title.perps_deposit',
26+
[TransactionType.perpsWithdraw]: 'transaction_details.title.perps_withdraw',
27+
[TransactionType.predictClaim]: 'transaction_details.title.predict_claim',
28+
[TransactionType.predictDeposit]: 'transaction_details.title.predict_deposit',
29+
[TransactionType.predictWithdraw]:
30+
'transaction_details.title.predict_withdraw',
31+
};
32+
33+
function getTitle(tx: TransactionMeta | undefined): string {
34+
const type =
35+
tx?.type === TransactionType.batch
36+
? ((tx.nestedTransactions?.find((n) => n.type && n.type in TITLE_KEYS)
37+
?.type as TransactionType | undefined) ?? tx.type)
38+
: tx?.type;
39+
return strings(
40+
(type && TITLE_KEYS[type]) ?? 'transaction_details.title.default',
41+
);
42+
}
43+
44+
const MoneyTransactionDetailsSheet = () => {
45+
const sheetRef = useRef<BottomSheetRef>(null);
46+
const navigation = useNavigation();
47+
const { transactionMeta } = useTransactionDetails();
48+
const title = getTitle(transactionMeta);
49+
50+
const handleClose = useCallback(() => {
51+
sheetRef.current?.onCloseBottomSheet();
52+
}, []);
53+
54+
return (
55+
<BottomSheet
56+
ref={sheetRef}
57+
isFullscreen
58+
goBack={navigation.goBack}
59+
keyboardAvoidingViewEnabled={false}
60+
>
61+
<BottomSheetHeader onClose={handleClose}>
62+
<Text variant={TextVariant.HeadingMd}>{title}</Text>
63+
</BottomSheetHeader>
64+
<TransactionDetails />
65+
</BottomSheet>
66+
);
67+
};
68+
69+
export default MoneyTransactionDetailsSheet;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './MoneyTransactionDetailsSheet';

0 commit comments

Comments
 (0)