diff --git a/.github/workflows/size-compare.yml b/.github/workflows/size-compare.yml index eeec796c1a8..008f98a7645 100644 --- a/.github/workflows/size-compare.yml +++ b/.github/workflows/size-compare.yml @@ -25,7 +25,7 @@ jobs: pull-requests: write steps: - name: Wait for ${{github.base_ref}} build to succeed - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@v1.2.0 id: master-build with: token: ${{ secrets.GITHUB_TOKEN }} @@ -33,7 +33,7 @@ jobs: ref: ${{github.base_ref}} - name: Wait for PR build to succeed - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@v1.2.0 id: wait-for-build with: token: ${{ secrets.GITHUB_TOKEN }} @@ -46,7 +46,7 @@ jobs: echo "Build failed on PR branch or ${{github.base_ref}}" exit 1 - name: Download build artifact from ${{github.base_ref}} - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 id: pr-build with: branch: ${{github.base_ref}} @@ -55,12 +55,13 @@ jobs: path: base - name: Download build artifact from PR - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: pr: ${{github.event.pull_request.number}} workflow: build.yml name: build-stats path: head + allow_forks: true - name: Strip content hashes from stats files run: | @@ -70,14 +71,14 @@ jobs: sed -i -E 's/index\.[0-9a-zA-Z_-]{8,}\./index./g' ./base/web-stats.json sed -i -E 's/\.[0-9a-zA-Z_-]{8,}\.chunk\././g' ./base/web-stats.json sed -i -E 's/\.[0-9a-f]{8,}\././g' ./base/*.json - - uses: twk3/rollup-size-compare-action@v1.0.0 + - uses: twk3/rollup-size-compare-action@v1.1.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} current-stats-json-path: ./head/web-stats.json base-stats-json-path: ./base/web-stats.json title: desktop-client - - uses: github/webpack-bundlesize-compare-action@v1.8.2 + - uses: github/webpack-bundlesize-compare-action@v2.1.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} current-stats-json-path: ./head/loot-core-stats.json diff --git a/.gitignore b/.gitignore index 25e14cb305c..ed99d63bd4c 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ bundle.mobile.js.map # Local Netlify folder .netlify + +# build output +package.tgz diff --git a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png index 984b80b5728..c0af58e5efa 100644 Binary files a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png and b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png index 06005e54628..2e97ccad230 100644 Binary files a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png and b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png index dd02417d9b9..32c1e063f51 100644 Binary files a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png and b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Envelope-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png index bdb7e1a9fc7..7f442d933e2 100644 Binary files a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png and b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png index 55c39e9fc41..0025f5e29c7 100644 Binary files a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png and b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png index 23d1ea1afd3..aadd6efa3d0 100644 Binary files a/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png and b/packages/desktop-client/e2e/budget.mobile.test.js-snapshots/Mobile-Budget-Tracking-checks-that-clicking-the-budgeted-cell-opens-the-budget-menu-modal-3-chromium-linux.png differ diff --git a/packages/desktop-client/package.tgz b/packages/desktop-client/package.tgz deleted file mode 100644 index 3efd4222666..00000000000 Binary files a/packages/desktop-client/package.tgz and /dev/null differ diff --git a/packages/desktop-client/src/browser-preload.browser.js b/packages/desktop-client/src/browser-preload.browser.js index 2522fce9065..a65e4b09a24 100644 --- a/packages/desktop-client/src/browser-preload.browser.js +++ b/packages/desktop-client/src/browser-preload.browser.js @@ -151,6 +151,8 @@ global.Actual = { setTheme: theme => { window.__actionsForMenu.saveGlobalPrefs({ theme }); }, + + moveBudgetDirectory: () => {}, }; function inputFocused(e) { diff --git a/packages/desktop-client/src/components/EditablePageHeaderTitle.tsx b/packages/desktop-client/src/components/EditablePageHeaderTitle.tsx new file mode 100644 index 00000000000..0552bb4bb33 --- /dev/null +++ b/packages/desktop-client/src/components/EditablePageHeaderTitle.tsx @@ -0,0 +1,89 @@ +import React, { useCallback, useEffect, useState } from 'react'; + +import { SvgPencil1 } from '../icons/v2'; +import { theme } from '../style'; + +import { Button } from './common/Button2'; +import { InitialFocus } from './common/InitialFocus'; +import { Input } from './common/Input'; +import { View } from './common/View'; + +type EditablePageHeaderTitleProps = { + title: string; + onSave: (newValue: string) => void; +}; + +export function EditablePageHeaderTitle({ + title: initialTitle, + onSave, +}: EditablePageHeaderTitleProps) { + const [isEditing, setIsEditing] = useState(false); + const [title, setTitle] = useState(initialTitle); + + useEffect(() => setTitle(initialTitle), [initialTitle]); + + const onSaveValue = useCallback( + (newValue: string) => { + onSave(newValue); + setTitle(newValue); + setIsEditing(false); + }, + [onSave], + ); + + if (isEditing) { + return ( + + onSaveValue(e.currentTarget.value)} + onBlur={e => onSaveValue(e.target.value)} + onEscape={() => setIsEditing(false)} + style={{ + fontSize: 25, + fontWeight: 500, + marginTop: -3, + marginBottom: -3, + marginLeft: -6, + paddingTop: 2, + paddingBottom: 2, + width: Math.max(20, title.length) + 'ch', + }} + /> + + ); + } + + return ( + + {title} + + + + ); +} diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index 19ca95755e5..59af5cd24a2 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -41,7 +41,9 @@ import { HoldBufferModal } from './modals/HoldBufferModal'; import { ImportTransactionsModal } from './modals/ImportTransactionsModal'; import { KeyboardShortcutModal } from './modals/KeyboardShortcutModal'; import { LoadBackupModal } from './modals/LoadBackupModal'; +import { ConfirmChangeDocumentDirModal } from './modals/manager/ConfirmChangeDocumentDir'; import { DeleteFileModal } from './modals/manager/DeleteFileModal'; +import { FilesSettingsModal } from './modals/manager/FilesSettingsModal'; import { ImportActualModal } from './modals/manager/ImportActualModal'; import { ImportModal } from './modals/manager/ImportModal'; import { ImportYNAB4Modal } from './modals/manager/ImportYNAB4Modal'; @@ -573,6 +575,16 @@ export function Modals() { return ; case 'import': return ; + case 'files-settings': + return ; + case 'confirm-change-document-dir': + return ( + + ); case 'import-ynab4': return ; case 'import-ynab5': diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 9e23555b190..92ab8a2f3e4 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -486,8 +486,12 @@ class AccountInternal extends PureComponent< this.paged.unsubscribe(); } - // Filter out reconciled transactions if necessary. - if (!this.state.showReconciled) { + // Filter out reconciled transactions if they are hidden + // and we're not showing balances. + if ( + !this.state.showReconciled && + (!this.state.showBalances || !this.canCalculateBalance()) + ) { query = query.filter({ reconciled: { $eq: false } }); } @@ -1746,6 +1750,7 @@ class AccountInternal extends PureComponent< payees={payees} balances={allBalances} showBalances={!!allBalances} + showReconciled={showReconciled} showCleared={showCleared} showAccount={ !accountId || diff --git a/packages/desktop-client/src/components/alerts.tsx b/packages/desktop-client/src/components/alerts.tsx index 831dc52a68f..7cb177b8f78 100644 --- a/packages/desktop-client/src/components/alerts.tsx +++ b/packages/desktop-client/src/components/alerts.tsx @@ -66,9 +66,9 @@ export const Information = ({ style, children }: ScopedAlertProps) => { color={theme.pageTextLight} backgroundColor="transparent" style={{ - ...style, boxShadow: 'none', padding: 5, + ...style, }} > {children} diff --git a/packages/desktop-client/src/components/manager/BudgetList.tsx b/packages/desktop-client/src/components/manager/BudgetList.tsx index c12700fa9a4..1cd0f5e3c96 100644 --- a/packages/desktop-client/src/components/manager/BudgetList.tsx +++ b/packages/desktop-client/src/components/manager/BudgetList.tsx @@ -12,7 +12,10 @@ import { loadBudget, pushModal, } from 'loot-core/client/actions'; -import { isNonProductionEnvironment } from 'loot-core/src/shared/environment'; +import { + isElectron, + isNonProductionEnvironment, +} from 'loot-core/src/shared/environment'; import { type File, type LocalFile, @@ -26,6 +29,7 @@ import { AnimatedLoading } from '../../icons/AnimatedLoading'; import { SvgCloudCheck, SvgCloudDownload, + SvgCog, SvgDotsHorizontalTriple, SvgFileDouble, } from '../../icons/v1'; @@ -78,24 +82,8 @@ function FileMenu({ const { t } = useTranslation(); const items = [{ name: 'delete', text: t('Delete') }]; - const { isNarrowWidth } = useResponsive(); - - const defaultMenuItemStyle = isNarrowWidth - ? { - ...styles.mobileMenuItem, - color: theme.menuItemText, - borderRadius: 0, - borderTop: `1px solid ${theme.pillBorder}`, - } - : {}; - return ( - defaultMenuItemStyle} - onMenuSelect={onMenuSelect} - items={items} - /> - ); + return ; } function FileMenuButton({ onDelete }: { onDelete: () => void }) { @@ -202,50 +190,60 @@ function FileItem({ } return ( - _onSelect(file)} - title={getFileDescription(file, t) || ''} + ); } @@ -330,12 +328,33 @@ function RefreshButton({ ); } +function SettingsButton({ onOpenSettings }: { onOpenSettings: () => void }) { + const { t } = useTranslation(); + + return ( + + + + ); +} + function BudgetListHeader({ quickSwitchMode, onRefresh, + onOpenSettings, }: { quickSwitchMode: boolean; onRefresh: () => void; + onOpenSettings: () => void; }) { return ( Files - {!quickSwitchMode && } + {!quickSwitchMode && ( + + + {isElectron() && } + + )} ); } @@ -431,6 +460,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { dispatch(pushModal('files-settings'))} /> )} @@ -461,82 +467,101 @@ const TransactionEditInner = memo(function TransactionEditInner({ useSingleActiveEditForm(); const [totalAmountFocused, setTotalAmountFocused] = useState(true); const childTransactionElementRefMap = useRef({}); + const hasAccountChanged = useRef(false); const payeesById = useMemo(() => groupById(payees), [payees]); const accountsById = useMemo(() => groupById(accounts), [accounts]); - const onTotalAmountEdit = () => { + const onTotalAmountEdit = useCallback(() => { onRequestActiveEdit?.(getFieldName(transaction.id, 'amount'), () => { setTotalAmountFocused(true); return () => setTotalAmountFocused(false); }); - }; + }, [onRequestActiveEdit, transaction.id]); + + const isInitialMount = useInitialMount(); useEffect(() => { - if (adding) { + if (isInitialMount && adding) { onTotalAmountEdit(); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [adding, isInitialMount, onTotalAmountEdit]); - const getAccount = trans => { - return trans?.account && accountsById?.[trans.account]; - }; - - const getPayee = trans => { - return trans?.payee && payeesById?.[trans.payee]; - }; + const getAccount = useCallback( + trans => { + return trans?.account && accountsById?.[trans.account]; + }, + [accountsById], + ); - const getTransferAcct = trans => { - const payee = trans && getPayee(trans); - return payee?.transfer_acct && accountsById?.[payee.transfer_acct]; - }; + const getPayee = useCallback( + trans => { + return trans?.payee && payeesById?.[trans.payee]; + }, + [payeesById], + ); - const getPrettyPayee = trans => { - if (trans && trans.is_parent) { - return 'Split'; - } - const transPayee = trans && getPayee(trans); - const transTransferAcct = trans && getTransferAcct(trans); - return getDescriptionPretty(trans, transPayee, transTransferAcct); - }; + const getTransferAcct = useCallback( + trans => { + const payee = trans && getPayee(trans); + return payee?.transfer_acct && accountsById?.[payee.transfer_acct]; + }, + [accountsById, getPayee], + ); - const isBudgetTransfer = trans => { - const transferAcct = trans && getTransferAcct(trans); - return transferAcct && !transferAcct.offbudget; - }; + const getPrettyPayee = useCallback( + trans => { + if (trans?.is_parent) { + return 'Split'; + } + const transPayee = trans && getPayee(trans); + const transTransferAcct = trans && getTransferAcct(trans); + return getDescriptionPretty(trans, transPayee, transTransferAcct); + }, + [getPayee, getTransferAcct], + ); - const getCategory = (trans, isOffBudget) => { - return isOffBudget - ? 'Off Budget' - : isBudgetTransfer(trans) - ? 'Transfer' - : lookupName(categories, trans.category); - }; + const isBudgetTransfer = useCallback( + trans => { + const transferAcct = trans && getTransferAcct(trans); + return transferAcct && !transferAcct.offbudget; + }, + [getTransferAcct], + ); - const onTotalAmountUpdate = value => { - if (transaction.amount !== value) { - onUpdate(transaction, 'amount', value.toString()); - } else { - onClearActiveEdit(); - } - }; + const getCategory = useCallback( + (trans, isOffBudget) => { + if (isOffBudget) { + return 'Off Budget'; + } else if (isBudgetTransfer(trans)) { + return 'Transfer'; + } else { + return lookupName(categories, trans.category); + } + }, + [categories, isBudgetTransfer], + ); - const onSave = async () => { + const onSaveInner = useCallback(() => { const [unserializedTransaction] = unserializedTransactions; - const onConfirmSave = async () => { + const onConfirmSave = () => { let transactionsToSave = unserializedTransactions; if (adding) { transactionsToSave = realizeTempTransactions(unserializedTransactions); } - props.onSave(transactionsToSave); + onSave(transactionsToSave); - if (adding) { + if (adding || hasAccountChanged.current) { const { account: accountId } = unserializedTransaction; - const account = accountsById[accountId]; - navigate(`/accounts/${account.id}`, { replace: true }); + const account = accountsById?.[accountId]; + if (account) { + navigate(`/accounts/${account.id}`); + } else { + // Handle the case where account is undefined + navigate(-1); + } } else { navigate(-1); } @@ -556,133 +581,166 @@ const TransactionEditInner = memo(function TransactionEditInner({ } else { onConfirmSave(); } - }; - - const onAdd = () => { - onSave(); - }; + }, [ + accountsById, + adding, + dispatch, + navigate, + onSave, + unserializedTransactions, + ]); + + const onUpdateInner = useCallback( + async (serializedTransaction, name, value) => { + const newTransaction = { ...serializedTransaction, [name]: value }; + await onUpdate(newTransaction, name); + onClearActiveEdit(); - const onUpdate = async (serializedTransaction, name, value) => { - const newTransaction = { ...serializedTransaction, [name]: value }; - await props.onUpdate(newTransaction, name); - onClearActiveEdit(); - }; + if (name === 'account') { + hasAccountChanged.current = serializedTransaction.account !== value; + } + }, + [onClearActiveEdit, onUpdate], + ); - const onEditField = (transactionId, name) => { - onRequestActiveEdit?.(getFieldName(transaction.id, name), () => { - const transactionToEdit = transactions.find(t => t.id === transactionId); - const unserializedTransaction = unserializedTransactions.find( - t => t.id === transactionId, - ); - switch (name) { - case 'category': - dispatch( - pushModal('category-autocomplete', { - categoryGroups, - month: monthUtils.monthFromDate(unserializedTransaction.date), - onSelect: categoryId => { - onUpdate(transactionToEdit, name, categoryId); - }, - onClose: () => { - onClearActiveEdit(); - }, - }), - ); - break; - case 'account': - dispatch( - pushModal('account-autocomplete', { - onSelect: accountId => { - onUpdate(transactionToEdit, name, accountId); - }, - onClose: () => { - onClearActiveEdit(); - }, - }), - ); - break; - case 'payee': - dispatch( - pushModal('payee-autocomplete', { - onSelect: payeeId => { - onUpdate(transactionToEdit, name, payeeId); - }, - onClose: () => { - onClearActiveEdit(); - }, - }), - ); - break; - default: - dispatch( - pushModal('edit-field', { - name, - month: monthUtils.monthFromDate(unserializedTransaction.date), - onSubmit: (name, value) => { - onUpdate(transactionToEdit, name, value); - }, - onClose: () => { - onClearActiveEdit(); - }, - }), - ); - break; + const onTotalAmountUpdate = useCallback( + value => { + if (transaction.amount !== value) { + onUpdateInner(transaction, 'amount', value.toString()); + } else { + onClearActiveEdit(); } - }); - }; + }, + [onClearActiveEdit, onUpdateInner, transaction], + ); - const onDelete = id => { - const [unserializedTransaction] = unserializedTransactions; + const onEditFieldInner = useCallback( + (transactionId, name) => { + onRequestActiveEdit?.(getFieldName(transaction.id, name), () => { + const transactionToEdit = transactions.find( + t => t.id === transactionId, + ); + const unserializedTransaction = unserializedTransactions.find( + t => t.id === transactionId, + ); + switch (name) { + case 'category': + dispatch( + pushModal('category-autocomplete', { + categoryGroups, + month: monthUtils.monthFromDate(unserializedTransaction.date), + onSelect: categoryId => { + onUpdateInner(transactionToEdit, name, categoryId); + }, + onClose: () => { + onClearActiveEdit(); + }, + }), + ); + break; + case 'account': + dispatch( + pushModal('account-autocomplete', { + onSelect: accountId => { + onUpdateInner(transactionToEdit, name, accountId); + }, + onClose: () => { + onClearActiveEdit(); + }, + }), + ); + break; + case 'payee': + dispatch( + pushModal('payee-autocomplete', { + onSelect: payeeId => { + onUpdateInner(transactionToEdit, name, payeeId); + }, + onClose: () => { + onClearActiveEdit(); + }, + }), + ); + break; + default: + dispatch( + pushModal('edit-field', { + name, + month: monthUtils.monthFromDate(unserializedTransaction.date), + onSubmit: (name, value) => { + onUpdateInner(transactionToEdit, name, value); + }, + onClose: () => { + onClearActiveEdit(); + }, + }), + ); + break; + } + }); + }, + [ + categoryGroups, + dispatch, + onUpdateInner, + onClearActiveEdit, + onRequestActiveEdit, + transaction.id, + transactions, + unserializedTransactions, + ], + ); - const onConfirmDelete = () => { - dispatch( - pushModal('confirm-transaction-delete', { - onConfirm: () => { - props.onDelete(id); - - if (unserializedTransaction.id !== id) { - // Only a child transaction was deleted. - onClearActiveEdit(); - return; - } + const onDeleteInner = useCallback( + id => { + const [unserializedTransaction] = unserializedTransactions; - navigate(-1); - }, - }), - ); - }; + const onConfirmDelete = () => { + dispatch( + pushModal('confirm-transaction-delete', { + onConfirm: () => { + onDelete(id); - if (unserializedTransaction.reconciled) { - dispatch( - pushModal('confirm-transaction-edit', { - onConfirm: onConfirmDelete, - confirmReason: 'deleteReconciled', - }), - ); - } else { - onConfirmDelete(); - } - }; + if (unserializedTransaction.id !== id) { + // Only a child transaction was deleted. + onClearActiveEdit(); + return; + } - const scrollChildTransactionIntoView = id => { + navigate(-1); + }, + }), + ); + }; + + if (unserializedTransaction.reconciled) { + dispatch( + pushModal('confirm-transaction-edit', { + onConfirm: onConfirmDelete, + confirmReason: 'deleteReconciled', + }), + ); + } else { + onConfirmDelete(); + } + }, + [dispatch, navigate, onClearActiveEdit, onDelete, unserializedTransactions], + ); + + const scrollChildTransactionIntoView = useCallback(id => { const childTransactionEditElement = childTransactionElementRefMap.current?.[id]; childTransactionEditElement?.scrollIntoView({ behavior: 'smooth', }); - }; - - const onAddSplit = id => { - props.onAddSplit(id); - }; - - const onSplit = id => { - props.onSplit(id); - }; + }, []); - const onEmptySplitFound = id => { - scrollChildTransactionIntoView(id); - }; + const onEmptySplitFound = useCallback( + id => { + scrollChildTransactionIntoView(id); + }, + [scrollChildTransactionIntoView], + ); useEffect(() => { const noAmountChildTransaction = childTransactions.find( @@ -691,7 +749,7 @@ const TransactionEditInner = memo(function TransactionEditInner({ if (noAmountChildTransaction) { scrollChildTransactionIntoView(noAmountChildTransaction.id); } - }, [childTransactions]); + }, [childTransactions, scrollChildTransactionIntoView]); // Child transactions should always default to the signage // of the parent transaction @@ -730,13 +788,13 @@ const TransactionEditInner = memo(function TransactionEditInner({