diff --git a/packages/desktop-client/e2e/accounts.test.ts b/packages/desktop-client/e2e/accounts.test.ts
index 16dd2591f4..18c6c9b743 100644
--- a/packages/desktop-client/e2e/accounts.test.ts
+++ b/packages/desktop-client/e2e/accounts.test.ts
@@ -123,11 +123,14 @@ test.describe('Accounts', () => {
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(join(__dirname, 'data/test.csv'));
- if (screenshot) await expect(page).toMatchThemeScreenshots();
-
const importButton = accountPage.page.getByRole('button', {
name: /Import \d+ transactions/,
});
+
+ await importButton.waitFor({ state: 'visible' });
+
+ if (screenshot) await expect(page).toMatchThemeScreenshots();
+
await importButton.click();
await expect(importButton).not.toBeVisible();
@@ -146,12 +149,14 @@ test.describe('Accounts', () => {
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(join(__dirname, 'data/test.csv'));
- await expect(page).toMatchThemeScreenshots();
-
const importButton = accountPage.page.getByRole('button', {
name: /Import \d+ transactions/,
});
+ await importButton.waitFor({ state: 'visible' });
+
+ await expect(page).toMatchThemeScreenshots();
+
await expect(importButton).toBeDisabled();
await expect(await importButton.innerText()).toMatch(
/Import 0 transactions/,
diff --git a/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.tsx b/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.tsx
index 8c2582b944..615d9100f7 100644
--- a/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.tsx
+++ b/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.tsx
@@ -381,7 +381,6 @@ export function ImportTransactionsModal({
return trans;
});
- setLoadingState(null);
setError(null);
/// Do fine grained reporting between the old and new OFX importers.
@@ -391,13 +390,8 @@ export function ImportTransactionsModal({
message: errors[0].message || 'Internal error',
});
} else {
- let flipAmount = false;
- let fieldMappings = null;
- let splitMode = false;
- let parseDateFormat: string | null = null;
-
if (filetype === 'csv' || filetype === 'qif') {
- flipAmount =
+ const flipAmount =
String(prefs[`flip-amount-${accountId}-${filetype}`]) === 'true';
setFlipAmount(flipAmount);
}
@@ -408,23 +402,22 @@ export function ImportTransactionsModal({
? JSON.parse(mappings)
: getInitialMappings(transactions);
- fieldMappings = mappings;
// @ts-expect-error - mappings might not have outflow/inflow properties
setFieldMappings(mappings);
// Set initial split mode based on any saved mapping
// @ts-expect-error - mappings might not have outflow/inflow properties
- splitMode = !!(mappings.outflow || mappings.inflow);
+ const splitMode = !!(mappings.outflow || mappings.inflow);
setSplitMode(splitMode);
- parseDateFormat =
+ const parseDateFormat =
prefs[`parse-date-${accountId}-${filetype}`] ||
getInitialDateFormat(transactions, mappings);
setParseDateFormat(
isDateFormat(parseDateFormat) ? parseDateFormat : null,
);
} else if (filetype === 'qif') {
- parseDateFormat =
+ const parseDateFormat =
prefs[`parse-date-${accountId}-${filetype}`] ||
getInitialDateFormat(transactions, { date: 'date' });
setParseDateFormat(
@@ -441,24 +434,12 @@ export function ImportTransactionsModal({
const reversedTransactions =
transactions.reverse() as ImportTransaction[];
setParsedTransactions(reversedTransactions);
-
- const transactionPreview = await getImportPreview(
- reversedTransactions,
- filetype,
- flipAmount,
- fieldMappings,
- splitMode,
- isDateFormat(parseDateFormat) ? parseDateFormat : null,
- inOutMode,
- outValue,
- multiplierAmount,
- );
- setTransactions(transactionPreview);
}
+
+ setLoadingState(null);
},
// We use some state variables from the component, but do not want to re-parse when they change
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [accountId, getImportPreview, prefs],
+ [accountId, prefs],
);
function onMultiplierChange(e) {
@@ -723,17 +704,6 @@ export function ImportTransactionsModal({
}
const runImportPreview = useCallback(async () => {
- // preserve user's selection choices before re-running preview
- const selectionMap = new Map();
- transactions.forEach(trans => {
- if (!trans.isMatchedTransaction) {
- selectionMap.set(trans.trx_id, {
- selected: trans.selected,
- selected_merge: trans.selected_merge,
- });
- }
- });
-
// always start from the original parsed transactions, not the previewed ones to ensure rules run
const transactionPreview = await getImportPreview(
parsedTransactions,
@@ -746,23 +716,7 @@ export function ImportTransactionsModal({
outValue,
multiplierAmount,
);
-
- // restore selections to the new preview results
- const transactionPreviewWithSelections = transactionPreview.map(trans => {
- if (!trans.isMatchedTransaction && selectionMap.has(trans.trx_id)) {
- const saved = selectionMap.get(trans.trx_id);
- return {
- ...trans,
- selected: saved.selected,
- selected_merge: saved.selected_merge,
- };
- }
- return trans;
- });
-
- setTransactions(transactionPreviewWithSelections);
- // intentionally exclude transactions from dependencies to avoid infinite rerenders
- // eslint-disable-next-line react-hooks/exhaustive-deps
+ setTransactions(transactionPreview);
}, [
getImportPreview,
parsedTransactions,
@@ -781,9 +735,7 @@ export function ImportTransactionsModal({
return;
}
- if (filetype === 'csv' || filetype === 'qif') {
- runImportPreview();
- }
+ runImportPreview();
// intentionally exclude runImportPreview from dependencies to avoid infinite rerenders
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
@@ -954,13 +906,6 @@ export function ImportTransactionsModal({
checked={fallbackMissingPayeeToMemo}
onChange={() => {
setFallbackMissingPayeeToMemo(state => !state);
- parse(
- filename,
- getParseOptions('ofx', {
- fallbackMissingPayeeToMemo: !fallbackMissingPayeeToMemo,
- importNotes,
- }),
- );
}}
>
Use Memo as a fallback for empty Payees
@@ -973,16 +918,6 @@ export function ImportTransactionsModal({
checked={importNotes}
onChange={() => {
setImportNotes(!importNotes);
- parse(
- filename,
- getParseOptions(filetype, {
- delimiter,
- hasHeaderRow,
- skipLines,
- fallbackMissingPayeeToMemo,
- importNotes: !importNotes,
- }),
- );
}}
>
Import notes from file
@@ -1047,15 +982,6 @@ export function ImportTransactionsModal({
value={delimiter}
onChange={value => {
setDelimiter(value);
- parse(
- filename,
- getParseOptions('csv', {
- delimiter: value,
- hasHeaderRow,
- skipLines,
- importNotes,
- }),
- );
}}
style={{ width: 50 }}
/>
@@ -1075,15 +1001,6 @@ export function ImportTransactionsModal({
min="0"
onChangeValue={value => {
setSkipLines(+value);
- parse(
- filename,
- getParseOptions('csv', {
- delimiter,
- hasHeaderRow,
- skipLines: +value,
- importNotes,
- }),
- );
}}
style={{ width: 50 }}
/>
@@ -1093,15 +1010,6 @@ export function ImportTransactionsModal({
checked={hasHeaderRow}
onChange={() => {
setHasHeaderRow(!hasHeaderRow);
- parse(
- filename,
- getParseOptions('csv', {
- delimiter,
- hasHeaderRow: !hasHeaderRow,
- skipLines,
- importNotes,
- }),
- );
}}
>
File has header row
diff --git a/upcoming-release-notes/5980.md b/upcoming-release-notes/5980.md
new file mode 100644
index 0000000000..b8b3da01e0
--- /dev/null
+++ b/upcoming-release-notes/5980.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [matt-fidd]
+---
+
+Fix slow performance in import csv modal