Skip to content

Commit 7ae6442

Browse files
authored
(typescript) Refactoring the mobile TransactionList component to typescript (#4063)
* refactor: transaction-list to ts * docs: add release note * address PR comments * typing getMenuItemStyle * refactor: added runtime check for NaN
1 parent 7744025 commit 7ae6442

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed

packages/desktop-client/src/components/common/Menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function Keybinding({ keyName }: KeybindingProps) {
3131
);
3232
}
3333

34-
type MenuItemObject<NameType, Type extends string | symbol = string> = {
34+
export type MenuItemObject<NameType, Type extends string | symbol = string> = {
3535
type?: Type;
3636
name: NameType;
3737
disabled?: boolean;

packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx renamed to packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, {
44
useMemo,
55
useRef,
66
useState,
7+
type CSSProperties,
78
} from 'react';
89
import { ListBox, Section, Header, Collection } from 'react-aria-components';
910
import { useTranslation } from 'react-i18next';
@@ -12,6 +13,7 @@ import { setNotificationInset } from 'loot-core/client/actions';
1213
import { groupById, integerToCurrency } from 'loot-core/shared/util';
1314
import * as monthUtils from 'loot-core/src/shared/months';
1415
import { isPreviewId } from 'loot-core/src/shared/transactions';
16+
import { type TransactionEntity } from 'loot-core/types/models/transaction';
1517

1618
import { useAccounts } from '../../../hooks/useAccounts';
1719
import { useCategories } from '../../../hooks/useCategories';
@@ -29,7 +31,7 @@ import { SvgDotsHorizontalTriple } from '../../../icons/v1';
2931
import { useDispatch } from '../../../redux';
3032
import { styles, theme } from '../../../style';
3133
import { Button } from '../../common/Button2';
32-
import { Menu } from '../../common/Menu';
34+
import { Menu, type MenuItemObject } from '../../common/Menu';
3335
import { Popover } from '../../common/Popover';
3436
import { Text } from '../../common/Text';
3537
import { View } from '../../common/View';
@@ -40,7 +42,12 @@ import { TransactionListItem } from './TransactionListItem';
4042

4143
const NOTIFICATION_BOTTOM_INSET = 75;
4244

43-
function Loading({ style, 'aria-label': ariaLabel }) {
45+
type LoadingProps = {
46+
style?: CSSProperties;
47+
'aria-label': string;
48+
};
49+
50+
function Loading({ style, 'aria-label': ariaLabel }: LoadingProps) {
4451
return (
4552
<View
4653
aria-label={ariaLabel || 'Loading...'}
@@ -57,17 +64,29 @@ function Loading({ style, 'aria-label': ariaLabel }) {
5764
);
5865
}
5966

67+
type TransactionListProps = {
68+
isLoading: boolean;
69+
transactions: readonly TransactionEntity[];
70+
onOpenTransaction?: (transaction: TransactionEntity) => void;
71+
isLoadingMore: boolean;
72+
onLoadMore: () => void;
73+
};
74+
6075
export function TransactionList({
6176
isLoading,
6277
transactions,
6378
onOpenTransaction,
6479
isLoadingMore,
6580
onLoadMore,
66-
}) {
81+
}: TransactionListProps) {
6782
const { t } = useTranslation();
6883
const sections = useMemo(() => {
6984
// Group by date. We can assume transactions is ordered
70-
const sections = [];
85+
const sections: {
86+
id: string;
87+
date: TransactionEntity['date'];
88+
transactions: TransactionEntity[];
89+
}[] = [];
7190
transactions.forEach(transaction => {
7291
if (
7392
sections.length === 0 ||
@@ -88,13 +107,16 @@ export function TransactionList({
88107
const dispatchSelected = useSelectedDispatch();
89108
const selectedTransactions = useSelectedItems();
90109

91-
const onTransactionPress = useCallback(
110+
const onTransactionPress: (
111+
transaction: TransactionEntity,
112+
isLongPress?: boolean,
113+
) => void = useCallback(
92114
(transaction, isLongPress = false) => {
93115
const isPreview = isPreviewId(transaction.id);
94116
if (!isPreview && (isLongPress || selectedTransactions.size > 0)) {
95117
dispatchSelected({ type: 'select', id: transaction.id });
96118
} else {
97-
onOpenTransaction(transaction);
119+
onOpenTransaction?.(transaction);
98120
}
99121
},
100122
[dispatchSelected, onOpenTransaction, selectedTransactions],
@@ -185,13 +207,21 @@ export function TransactionList({
185207
);
186208
}
187209

188-
function SelectedTransactionsFloatingActionBar({ transactions, style }) {
210+
type SelectedTransactionsFloatingActionBarProps = {
211+
transactions: readonly TransactionEntity[];
212+
style?: CSSProperties;
213+
};
214+
215+
function SelectedTransactionsFloatingActionBar({
216+
transactions,
217+
style = {},
218+
}: SelectedTransactionsFloatingActionBarProps) {
189219
const editMenuTriggerRef = useRef(null);
190220
const [isEditMenuOpen, setIsEditMenuOpen] = useState(false);
191221
const moreOptionsMenuTriggerRef = useRef(null);
192222
const [isMoreOptionsMenuOpen, setIsMoreOptionsMenuOpen] = useState(false);
193223
const getMenuItemStyle = useCallback(
194-
item => ({
224+
<T extends string>(item: MenuItemObject<T>) => ({
195225
...styles.mobileMenuItem,
196226
color: theme.mobileHeaderText,
197227
...(item.name === 'delete' && { color: theme.errorTextMenu }),
@@ -326,16 +356,20 @@ function SelectedTransactionsFloatingActionBar({ transactions, style }) {
326356
let displayValue = value;
327357
switch (name) {
328358
case 'account':
329-
displayValue = accountsById[value]?.name ?? value;
359+
displayValue =
360+
accountsById[String(value)]?.name ?? value;
330361
break;
331362
case 'category':
332-
displayValue = categoriesById[value]?.name ?? value;
363+
displayValue =
364+
categoriesById[String(value)]?.name ?? value;
333365
break;
334366
case 'payee':
335-
displayValue = payeesById[value]?.name ?? value;
367+
displayValue = payeesById[String(value)]?.name ?? value;
336368
break;
337369
case 'amount':
338-
displayValue = integerToCurrency(value);
370+
displayValue = Number.isNaN(Number(value))
371+
? value
372+
: integerToCurrency(Number(value));
339373
break;
340374
case 'notes':
341375
displayValue = `${mode} with ${value}`;

packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ const ROW_HEIGHT = 60;
4040
type TransactionListItemProps = ComponentPropsWithoutRef<
4141
typeof ListBoxItem<TransactionEntity>
4242
> & {
43-
isNewTransaction: (transaction: TransactionEntity['id']) => boolean;
4443
onPress: (transaction: TransactionEntity) => void;
4544
onLongPress: (transaction: TransactionEntity) => void;
4645
};

upcoming-release-notes/4063.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: Maintenance
3+
authors: [leoltl]
4+
---
5+
6+
Refactoring the mobile TransactionList component to typescript

0 commit comments

Comments
 (0)