mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 06:02:22 -05:00
Add refetchOnSync option to useTransactions to refetch when a server sync event is emitted (#6936)
* Migrate setupTests.js to TypeScript with proper types (#6871) * Initial plan * Rename setupTests.js to setupTests.ts and add proper types Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> * Extract Size type to avoid duplication Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> * Add release note for setupTests TypeScript migration Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> * Rename release note file to match PR number 6871 Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> * Delete setupTests PR release note * Add refetchOnSync to useTransactions to refetch when a server sync event is emitted * Add release note for useTransactions refetchOnSync feature (#6937) * Initial plan * Add release note for PR 6936 Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com> * Coderabbit feedback --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7fa9fa900b
commit
67d6592333
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { listen, send } from 'loot-core/platform/client/fetch';
|
||||
import { 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';
|
||||
@@ -87,7 +87,6 @@ function TransactionListWithPreviews({
|
||||
transactions,
|
||||
runningBalances,
|
||||
isPending: isTransactionsLoading,
|
||||
refetch: reloadTransactions,
|
||||
isFetchingNextPage: isLoadingMoreTransactions,
|
||||
fetchNextPage: fetchMoreTransactions,
|
||||
} = useTransactions({
|
||||
@@ -129,21 +128,6 @@ function TransactionListWithPreviews({
|
||||
}
|
||||
}, [account.id, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
return listen('sync-event', event => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
reloadTransactions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [dispatch, reloadTransactions]);
|
||||
|
||||
const onOpenTransaction = useCallback(
|
||||
(transaction: TransactionEntity) => {
|
||||
if (!isPreviewId(transaction.id)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { listen, send } from 'loot-core/platform/client/fetch';
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import { isPreviewId } from 'loot-core/shared/transactions';
|
||||
import type { TransactionEntity } from 'loot-core/types/models';
|
||||
@@ -42,7 +42,6 @@ function TransactionListWithPreviews() {
|
||||
const {
|
||||
transactions,
|
||||
isPending: isTransactionsLoading,
|
||||
refetch: reloadTransactions,
|
||||
isFetchingNextPage: isLoadingMoreTransactions,
|
||||
fetchNextPage: fetchMoreTransactions,
|
||||
} = useTransactions({
|
||||
@@ -55,21 +54,6 @@ function TransactionListWithPreviews() {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
return listen('sync-event', event => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
reloadTransactions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [reloadTransactions]);
|
||||
|
||||
const { isSearching, search: onSearch } = useTransactionsSearch({
|
||||
updateQuery: setTransactionsQuery,
|
||||
resetQuery: () => setTransactionsQuery(baseTransactionsQuery()),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { listen, send } from 'loot-core/platform/client/fetch';
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import { isPreviewId } from 'loot-core/shared/transactions';
|
||||
import type { ScheduleEntity, TransactionEntity } from 'loot-core/types/models';
|
||||
@@ -44,7 +44,6 @@ function TransactionListWithPreviews() {
|
||||
const {
|
||||
transactions,
|
||||
isPending: isTransactionsLoading,
|
||||
refetch: reloadTransactions,
|
||||
isFetchingNextPage: isLoadingMoreTransactions,
|
||||
fetchNextPage: fetchMoreTransactions,
|
||||
} = useTransactions({
|
||||
@@ -66,21 +65,6 @@ function TransactionListWithPreviews() {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
return listen('sync-event', event => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
reloadTransactions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [reloadTransactions]);
|
||||
|
||||
const { isSearching, search: onSearch } = useTransactionsSearch({
|
||||
updateQuery: setTransactionsQuery,
|
||||
resetQuery: () => setTransactionsQuery(baseTransactionsQuery()),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { listen, send } from 'loot-core/platform/client/fetch';
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import { isPreviewId } from 'loot-core/shared/transactions';
|
||||
import type { ScheduleEntity, TransactionEntity } from 'loot-core/types/models';
|
||||
@@ -44,7 +44,6 @@ function TransactionListWithPreviews() {
|
||||
const {
|
||||
transactions,
|
||||
isPending: isTransactionsLoading,
|
||||
refetch: reloadTransactions,
|
||||
isFetchingNextPage: isLoadingMoreTransactions,
|
||||
fetchNextPage: fetchMoreTransactions,
|
||||
} = useTransactions({
|
||||
@@ -66,21 +65,6 @@ function TransactionListWithPreviews() {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
return listen('sync-event', event => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
reloadTransactions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [reloadTransactions]);
|
||||
|
||||
const { isSearching, search: onSearch } = useTransactionsSearch({
|
||||
updateQuery: setTransactionsQuery,
|
||||
resetQuery: () => setTransactionsQuery(baseTransactionsQuery()),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { listen } from 'loot-core/platform/client/fetch';
|
||||
import { q } from 'loot-core/shared/query';
|
||||
import { isPreviewId } from 'loot-core/shared/transactions';
|
||||
import type { CategoryEntity, TransactionEntity } from 'loot-core/types/models';
|
||||
@@ -12,7 +11,6 @@ import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useTransactions } from '@desktop-client/hooks/useTransactions';
|
||||
import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSearch';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
type CategoryTransactionsProps = {
|
||||
@@ -42,7 +40,6 @@ function TransactionListWithPreviews({
|
||||
category,
|
||||
month,
|
||||
}: TransactionListWithPreviewsProps) {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const baseTransactionsQuery = useCallback(
|
||||
@@ -62,28 +59,12 @@ function TransactionListWithPreviews({
|
||||
isPending: isTransactionsLoading,
|
||||
isFetchingNextPage: isLoadingMoreTransactions,
|
||||
fetchNextPage: fetchMoreTransactions,
|
||||
refetch: reloadTransactions,
|
||||
} = useTransactions({
|
||||
query: transactionsQuery,
|
||||
});
|
||||
|
||||
const dateFormat = useDateFormat() || 'MM/dd/yyyy';
|
||||
|
||||
useEffect(() => {
|
||||
return listen('sync-event', event => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
reloadTransactions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [dispatch, reloadTransactions]);
|
||||
|
||||
const { isSearching, search: onSearch } = useTransactionsSearch({
|
||||
updateQuery: setTransactionsQuery,
|
||||
resetQuery: () => setTransactionsQuery(baseTransactionsQuery()),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { listen } from 'loot-core/platform/client/fetch';
|
||||
import { isPreviewId } from 'loot-core/shared/transactions';
|
||||
import type { TransactionEntity } from 'loot-core/types/models';
|
||||
|
||||
@@ -11,11 +10,9 @@ import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useTransactions } from '@desktop-client/hooks/useTransactions';
|
||||
import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSearch';
|
||||
import { uncategorizedTransactions } from '@desktop-client/queries';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
export function UncategorizedTransactions() {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const baseTransactionsQuery = useCallback(
|
||||
() => uncategorizedTransactions().options({ splits: 'inline' }).select('*'),
|
||||
@@ -30,28 +27,12 @@ export function UncategorizedTransactions() {
|
||||
isPending: isTransactionsLoading,
|
||||
isFetchingNextPage: isLoadingMoreTransactions,
|
||||
fetchNextPage: fetchMoreTransactions,
|
||||
refetch: reloadTransactions,
|
||||
} = useTransactions({
|
||||
query: transactionsQuery,
|
||||
});
|
||||
|
||||
const dateFormat = useDateFormat() || 'MM/dd/yyyy';
|
||||
|
||||
useEffect(() => {
|
||||
return listen('sync-event', event => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
reloadTransactions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [dispatch, reloadTransactions]);
|
||||
|
||||
const { search: onSearch } = useTransactionsSearch({
|
||||
updateQuery: setTransactionsQuery,
|
||||
resetQuery: () => setTransactionsQuery(baseTransactionsQuery()),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useEffectEvent, useState } from 'react';
|
||||
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import type {
|
||||
@@ -6,9 +6,11 @@ import type {
|
||||
UseInfiniteQueryResult,
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
import { listen } from 'loot-core/platform/client/fetch';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import type { IntegerAmount } from 'loot-core/shared/util';
|
||||
import type { TransactionEntity } from 'loot-core/types/models';
|
||||
import type { ServerEvents } from 'loot-core/types/server-events';
|
||||
|
||||
import { transactionQueries } from '@desktop-client/transactions';
|
||||
|
||||
@@ -62,6 +64,12 @@ type UseTransactionsProps = {
|
||||
* @default 0
|
||||
*/
|
||||
startingBalance?: IntegerAmount;
|
||||
|
||||
/**
|
||||
* Whether to refetch transactions when a sync event is emitted.
|
||||
* @default true
|
||||
*/
|
||||
refetchOnSync?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -83,20 +91,46 @@ type UseTransactionsResult = UseInfiniteQueryResult<
|
||||
|
||||
export function useTransactions({
|
||||
query,
|
||||
options = { pageSize: 50, calculateRunningBalances: false },
|
||||
options,
|
||||
}: UseTransactionsProps): UseTransactionsResult {
|
||||
const {
|
||||
pageSize = 50,
|
||||
calculateRunningBalances = false,
|
||||
startingBalance,
|
||||
refetchOnSync = true,
|
||||
} = options ?? {};
|
||||
|
||||
const [runningBalances, setRunningBalances] = useState<
|
||||
Map<TransactionEntity['id'], IntegerAmount>
|
||||
>(new Map());
|
||||
|
||||
const queryResult = useInfiniteQuery(
|
||||
transactionQueries.aql({ query, pageSize: options.pageSize }),
|
||||
transactionQueries.aql({ query, pageSize }),
|
||||
);
|
||||
|
||||
const onSyncEvent = useEffectEvent((event: ServerEvents['sync-event']) => {
|
||||
if (event.type === 'applied') {
|
||||
const tables = event.tables;
|
||||
if (
|
||||
tables.includes('transactions') ||
|
||||
tables.includes('category_mapping') ||
|
||||
tables.includes('payee_mapping')
|
||||
) {
|
||||
queryResult.refetch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!refetchOnSync) {
|
||||
return;
|
||||
}
|
||||
return listen('sync-event', onSyncEvent);
|
||||
}, [refetchOnSync]);
|
||||
|
||||
const calculateRunningBalancesOptionFn = getCalculateRunningBalancesFn(
|
||||
options?.calculateRunningBalances,
|
||||
calculateRunningBalances,
|
||||
);
|
||||
const startingBalanceOption = options?.startingBalance;
|
||||
const splitsOption = query?.state.tableOptions
|
||||
?.splits as TransactionSplitsOption;
|
||||
|
||||
@@ -108,12 +142,12 @@ export function useTransactions({
|
||||
useEffect(() => {
|
||||
if (calculateRunningBalancesOptionFn) {
|
||||
if (queryResult.isSuccess) {
|
||||
const transactions = queryResult.data.pages.flat();
|
||||
const transactions = flattenPages(queryResult.data);
|
||||
setRunningBalances(
|
||||
calculateRunningBalancesOptionFn(
|
||||
transactions,
|
||||
splitsOption,
|
||||
startingBalanceOption,
|
||||
startingBalance,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -124,13 +158,13 @@ export function useTransactions({
|
||||
queryResult.data,
|
||||
queryResult.isSuccess,
|
||||
calculateRunningBalancesOptionFn,
|
||||
startingBalanceOption,
|
||||
startingBalance,
|
||||
splitsOption,
|
||||
]);
|
||||
|
||||
return {
|
||||
...queryResult,
|
||||
transactions: queryResult.data ? queryResult.data.pages.flat() : [],
|
||||
transactions: flattenPages(queryResult.data),
|
||||
runningBalances,
|
||||
};
|
||||
}
|
||||
@@ -224,3 +258,9 @@ export function calculateRunningBalancesTopDown(
|
||||
return acc;
|
||||
}, new Map<TransactionEntity['id'], IntegerAmount>());
|
||||
}
|
||||
|
||||
function flattenPages(
|
||||
data?: InfiniteData<TransactionEntity[]>,
|
||||
): TransactionEntity[] {
|
||||
return data ? data.pages.flat() : [];
|
||||
}
|
||||
|
||||
6
upcoming-release-notes/6936.md
Normal file
6
upcoming-release-notes/6936.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [joel-jeremy]
|
||||
---
|
||||
|
||||
Add refetchOnSync option to useTransactions hook to consolidate duplicate sync-event handling logic.
|
||||
Reference in New Issue
Block a user