diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountPage.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountPage.tsx index 798350fd91..9856e4a811 100644 --- a/packages/desktop-client/src/components/mobile/accounts/AccountPage.tsx +++ b/packages/desktop-client/src/components/mobile/accounts/AccountPage.tsx @@ -27,6 +27,7 @@ import { useAccount } from '@desktop-client/hooks/useAccount'; import { useFailedAccounts } from '@desktop-client/hooks/useFailedAccounts'; import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref'; import { + collapseModals, openAccountCloseModal, pushModal, } from '@desktop-client/modals/modalsSlice'; @@ -145,6 +146,18 @@ function AccountHeader({ account }: { readonly account: AccountEntity }) { dispatch(reopenAccount({ id: account.id })); }, [account.id, dispatch]); + const [showRunningBalances, setShowRunningBalances] = useSyncedPref( + `show-balances-${account.id}`, + ); + const onToggleRunningBalance = useCallback(() => { + setShowRunningBalances(showRunningBalances === 'true' ? 'false' : 'true'); + dispatch( + collapseModals({ + rootModalName: 'account-menu', + }), + ); + }, [showRunningBalances, setShowRunningBalances, dispatch]); + const onClick = useCallback(() => { dispatch( pushModal({ @@ -156,6 +169,7 @@ function AccountHeader({ account }: { readonly account: AccountEntity }) { onEditNotes, onCloseAccount, onReopenAccount, + onToggleRunningBalance, }, }, }), @@ -167,6 +181,7 @@ function AccountHeader({ account }: { readonly account: AccountEntity }) { onEditNotes, onReopenAccount, onSave, + onToggleRunningBalance, ]); return ( diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx index 97357d7c01..a324a62253 100644 --- a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { listen, send } from 'loot-core/platform/client/fetch'; import { type Query } from 'loot-core/shared/query'; import { isPreviewId } from 'loot-core/shared/transactions'; +import { type IntegerAmount } from 'loot-core/shared/util'; import { type AccountEntity, type TransactionEntity, @@ -17,6 +18,7 @@ import { SchedulesProvider } from '@desktop-client/hooks/useCachedSchedules'; import { useDateFormat } from '@desktop-client/hooks/useDateFormat'; import { useNavigate } from '@desktop-client/hooks/useNavigate'; import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules'; +import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref'; import { useTransactions } from '@desktop-client/hooks/useTransactions'; import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSearch'; import { collapseModals, pushModal } from '@desktop-client/modals/modalsSlice'; @@ -47,6 +49,9 @@ function TransactionListWithPreviews({ readonly account: AccountEntity; }) { const { t } = useTranslation(); + const dateFormat = useDateFormat() || 'MM/dd/yyyy'; + const dispatch = useDispatch(); + const navigate = useNavigate(); const baseTransactionsQuery = useCallback( () => @@ -54,27 +59,41 @@ function TransactionListWithPreviews({ [account.id], ); + const [showRunningBalances] = useSyncedPref(`show-balances-${account.id}`); const [transactionsQuery, setTransactionsQuery] = useState( baseTransactionsQuery(), ); + + const { isSearching, search: onSearch } = useTransactionsSearch({ + updateQuery: setTransactionsQuery, + resetQuery: () => setTransactionsQuery(baseTransactionsQuery()), + dateFormat, + }); + + const shouldCalculateRunningBalances = + showRunningBalances === 'true' && !!account?.id && !isSearching; + const { transactions, + runningBalances, isLoading: isTransactionsLoading, reload: reloadTransactions, isLoadingMore, loadMore: loadMoreTransactions, } = useTransactions({ query: transactionsQuery, + options: { + calculateRunningBalances: shouldCalculateRunningBalances, + }, }); - const { previewTransactions, isLoading: isPreviewTransactionsLoading } = - useAccountPreviewTransactions({ - accountId: account?.id, - }); - - const dateFormat = useDateFormat() || 'MM/dd/yyyy'; - const dispatch = useDispatch(); - const navigate = useNavigate(); + const { + previewTransactions, + runningBalances: previewRunningBalances, + isLoading: isPreviewTransactionsLoading, + } = useAccountPreviewTransactions({ + accountId: account?.id, + }); const onRefresh = useCallback(() => { if (account.id) { @@ -82,6 +101,15 @@ function TransactionListWithPreviews({ } }, [account.id, dispatch]); + const allBalances = useMemo( + () => + new Map([ + ...previewRunningBalances, + ...runningBalances, + ]), + [runningBalances, previewRunningBalances], + ); + useEffect(() => { if (account.id) { dispatch(markAccountRead({ id: account.id })); @@ -103,12 +131,6 @@ function TransactionListWithPreviews({ }); }, [dispatch, reloadTransactions]); - const { isSearching, search: onSearch } = useTransactionsSearch({ - updateQuery: setTransactionsQuery, - resetQuery: () => setTransactionsQuery(baseTransactionsQuery()), - dateFormat, - }); - const onOpenTransaction = useCallback( (transaction: TransactionEntity) => { if (!isPreviewId(transaction.id)) { @@ -178,12 +200,16 @@ function TransactionListWithPreviews({ return ( ; onOpenTransaction?: (transaction: TransactionEntity) => void; isLoadingMore: boolean; onLoadMore: () => void; @@ -92,6 +98,8 @@ type TransactionListProps = { export function TransactionList({ isLoading, transactions, + showRunningBalances, + runningBalances, onOpenTransaction, isLoadingMore, onLoadMore, @@ -174,7 +182,14 @@ export function TransactionList({ selectedTransactions.size > 0 ? 'multiple' : 'single' } selectedKeys={selectedTransactions} - dependencies={[selectedTransactions]} + dependencies={[ + selectedTransactions, + locale, + onTransactionPress, + runningBalances, + showRunningBalances, + t, + ]} renderEmptyState={() => !isLoading && ( ( onTransactionPress(trans)} onLongPress={trans => onTransactionPress(trans, true)} diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx b/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx index 778657e950..fd8e2d3955 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx @@ -30,7 +30,7 @@ import { } from '@react-aria/interactions'; import { isPreviewId } from 'loot-core/shared/transactions'; -import { integerToCurrency } from 'loot-core/shared/util'; +import { type IntegerAmount, integerToCurrency } from 'loot-core/shared/util'; import { type AccountEntity, type TransactionEntity, @@ -38,7 +38,10 @@ import { import { lookupName, Status } from './TransactionEdit'; -import { makeAmountFullStyle } from '@desktop-client/components/budget/util'; +import { + makeAmountFullStyle, + makeBalanceAmountStyle, +} from '@desktop-client/components/budget/util'; import { useAccount } from '@desktop-client/hooks/useAccount'; import { useCachedSchedules } from '@desktop-client/hooks/useCachedSchedules'; import { useCategories } from '@desktop-client/hooks/useCategories'; @@ -75,11 +78,15 @@ type TransactionListItemProps = Omit< ComponentPropsWithoutRef>, 'onPress' > & { + showRunningBalance?: boolean; + runningBalance?: IntegerAmount; onPress: (transaction: TransactionEntity) => void; onLongPress: (transaction: TransactionEntity) => void; }; export function TransactionListItem({ + showRunningBalance, + runningBalance, onPress, onLongPress, ...props @@ -286,7 +293,9 @@ export function TransactionListItem({ )} - + {integerToCurrency(amount)} + {showRunningBalance && runningBalance !== undefined && ( + + {integerToCurrency(runningBalance)} + + )} diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.tsx b/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.tsx index 5e28adfc2c..6ba38b7547 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.tsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.tsx @@ -6,6 +6,7 @@ import { styles } from '@actual-app/components/styles'; import { theme } from '@actual-app/components/theme'; import { View } from '@actual-app/components/view'; +import { type IntegerAmount } from 'loot-core/shared/util'; import { type TransactionEntity } from 'loot-core/types/models'; import { TransactionList } from './TransactionList'; @@ -84,6 +85,8 @@ type TransactionListWithBalancesProps = { balanceUncleared?: | Binding<'category', 'balanceUncleared'> | Binding<'account', 'balanceUncleared'>; + showRunningBalances?: boolean; + runningBalances?: Map; searchPlaceholder: string; onSearch: (searchText: string) => void; isLoadingMore: boolean; @@ -99,6 +102,8 @@ export function TransactionListWithBalances({ balance, balanceCleared, balanceUncleared, + showRunningBalances, + runningBalances, searchPlaceholder = 'Search...', onSearch, isLoadingMore, @@ -146,6 +151,8 @@ export function TransactionListWithBalances({ } title={ @@ -201,12 +204,14 @@ type AdditionalAccountMenuProps = { account: AccountEntity; onClose?: (accountId: string) => void; onReopen?: (accountId: string) => void; + onToggleRunningBalance?: () => void; }; function AdditionalAccountMenu({ account, onClose, onReopen, + onToggleRunningBalance, }: AdditionalAccountMenuProps) { const { t } = useTranslation(); const triggerRef = useRef(null); @@ -220,6 +225,7 @@ function AdditionalAccountMenu({ ...itemStyle, ...(item.name === 'close' && { color: theme.errorTextMenu }), }); + const [showBalances] = useSyncedPref(`show-balances-${account.id}`); return ( @@ -245,6 +251,13 @@ function AdditionalAccountMenu({ void; onEditNotes: (id: NoteEntity['id']) => void; onClose?: () => void; + onToggleRunningBalance?: () => void; }; } | { diff --git a/upcoming-release-notes/5513.md b/upcoming-release-notes/5513.md new file mode 100644 index 0000000000..35e056310a --- /dev/null +++ b/upcoming-release-notes/5513.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [youngcw,joel-jeremy] +--- + +Add back mobile running balance functionality