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.