diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx index 1d72fea856e..f7bb2e00b4e 100644 --- a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx @@ -234,6 +234,7 @@ function TransactionListWithPreviews({ transactions, isLoading, reload: reloadTransactions, + isLoadingMore, loadMore: loadMoreTransactions, } = useTransactions({ query: transactionsQuery, @@ -267,7 +268,7 @@ function TransactionListWithPreviews({ tables.includes('category_mapping') || tables.includes('payee_mapping') ) { - reloadTransactions?.(); + reloadTransactions(); } if (tables.includes('payees') || tables.includes('payee_mapping')) { @@ -324,6 +325,7 @@ function TransactionListWithPreviews({ balance={balanceQueries.balance} balanceCleared={balanceQueries.cleared} balanceUncleared={balanceQueries.uncleared} + isLoadingMore={isLoadingMore} onLoadMore={loadMoreTransactions} searchPlaceholder={`Search ${accountName}`} onSearch={onSearch} diff --git a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.jsx b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.jsx index c03f1676451..aa0df7912c0 100644 --- a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.jsx +++ b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.jsx @@ -40,6 +40,7 @@ export function CategoryTransactions({ category, month }) { const { transactions, isLoading, + isLoadingMore, loadMore: loadMoreTransactions, reload: reloadTransactions, } = useTransactions({ @@ -56,7 +57,7 @@ export function CategoryTransactions({ category, month }) { tables.includes('category_mapping') || tables.includes('payee_mapping') ) { - reloadTransactions?.(); + reloadTransactions(); } if (tables.includes('payees') || tables.includes('payee_mapping')) { @@ -112,6 +113,7 @@ export function CategoryTransactions({ category, month }) { balanceUncleared={balanceUncleared} searchPlaceholder={`Search ${category.name}`} onSearch={onSearch} + isLoadingMore={isLoadingMore} onLoadMore={loadMoreTransactions} onOpenTransaction={onOpenTransaction} /> diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx index a22e236f9af..81f45869c6e 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx @@ -6,10 +6,9 @@ import React, { useState, } from 'react'; import { ListBox, Section, Header, Collection } from 'react-aria-components'; +import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; -import { t } from 'i18next'; - import { setNotificationInset } from 'loot-core/client/actions'; import { groupById, integerToCurrency } from 'loot-core/shared/util'; import * as monthUtils from 'loot-core/src/shared/months'; @@ -41,12 +40,32 @@ import { TransactionListItem } from './TransactionListItem'; const NOTIFICATION_BOTTOM_INSET = 75; +function Loading({ style, 'aria-label': ariaLabel }) { + return ( + + + + ); +} + export function TransactionList({ isLoading, transactions, onOpenTransaction, + isLoadingMore, onLoadMore, }) { + const { t } = useTranslation(); + const sections = useMemo(() => { // Group by date. We can assume transactions is ordered const sections = []; @@ -83,29 +102,19 @@ export function TransactionList({ ); useScrollListener(({ hasScrolledToEnd }) => { - if (hasScrolledToEnd('down', 5)) { + if (hasScrolledToEnd('down', 100)) { onLoadMore?.(); } }); if (isLoading) { - return ( - - - - ); + return ; } + return ( <> 0 ? 'multiple' : 'single'} selectedKeys={selectedTransactions} dependencies={[selectedTransactions]} @@ -159,6 +168,17 @@ export function TransactionList({ )} + + {isLoadingMore && ( + + )} + {selectedTransactions.size > 0 && ( )} diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.jsx index f884f927ac1..b86c131a901 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.jsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionListWithBalances.jsx @@ -65,6 +65,7 @@ export function TransactionListWithBalances({ balanceUncleared, searchPlaceholder = 'Search...', onSearch, + isLoadingMore, onLoadMore, onOpenTransaction, onRefresh, @@ -104,6 +105,7 @@ export function TransactionListWithBalances({ diff --git a/packages/loot-core/src/client/data-hooks/transactions.ts b/packages/loot-core/src/client/data-hooks/transactions.ts index 60ec93e1f2d..096fe2836ea 100644 --- a/packages/loot-core/src/client/data-hooks/transactions.ts +++ b/packages/loot-core/src/client/data-hooks/transactions.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState, useMemo } from 'react'; +import { useEffect, useRef, useState, useMemo, useCallback } from 'react'; import debounce from 'lodash/debounce'; @@ -24,10 +24,11 @@ type UseTransactionsProps = { type UseTransactionsResult = { transactions: ReadonlyArray; - isLoading?: boolean; + isLoading: boolean; error?: Error; - reload?: () => void; - loadMore?: () => void; + reload: () => void; + loadMore: () => void; + isLoadingMore: boolean; }; export function useTransactions({ @@ -35,6 +36,7 @@ export function useTransactions({ options = { pageCount: 50 }, }: UseTransactionsProps): UseTransactionsResult { const [isLoading, setIsLoading] = useState(true); + const [isLoadingMore, setIsLoadingMore] = useState(false); const [error, setError] = useState(undefined); const [transactions, setTransactions] = useState< ReadonlyArray @@ -88,12 +90,32 @@ export function useTransactions({ }; }, [query]); + const loadMore = useCallback(async () => { + if (!pagedQueryRef.current) { + return; + } + + setIsLoadingMore(true); + + await pagedQueryRef.current + .fetchNext() + .catch(setError) + .finally(() => { + setIsLoadingMore(false); + }); + }, []); + + const reload = useCallback(() => { + pagedQueryRef.current?.run(); + }, []); + return { transactions, isLoading, error, - reload: pagedQueryRef.current?.run, - loadMore: pagedQueryRef.current?.fetchNext, + reload, + loadMore, + isLoadingMore, }; } diff --git a/upcoming-release-notes/3900.md b/upcoming-release-notes/3900.md new file mode 100644 index 00000000000..6482218bc0c --- /dev/null +++ b/upcoming-release-notes/3900.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [joel-jeremy] +--- + +Add loading indicator when loading more transactions in mobile transaction list.