Skip to content

Commit

Permalink
♻️ (typescript) Refactor Accounts/Balances to tsx and Remove ts-stric…
Browse files Browse the repository at this point in the history
…t-ignore from Accounts/Account (#4047)

* Convert Balance.jsx to Balance.tsx

* Removed @ts-strict-ignore from Account.tsx

* Create 4047.md

* Fix typo

* Added Translation helpers to aria-labels

* Clarified canCalculateBalance return value logic
  • Loading branch information
tlesicka authored Jan 8, 2025
1 parent 3fbe6d0 commit ce0ca60
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 75 deletions.
80 changes: 42 additions & 38 deletions packages/desktop-client/src/components/accounts/Account.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React, {
PureComponent,
type MutableRefObject,
Expand Down Expand Up @@ -332,8 +331,8 @@ class AccountInternal extends PureComponent<
AccountInternalState
> {
paged: PagedQuery<TransactionEntity> | null;
rootQuery: Query;
currentQuery: Query;
rootQuery!: Query;
currentQuery!: Query;
table: TableRef;
unlisten?: () => void;
dispatchSelected?: (action: Actions) => void;
Expand Down Expand Up @@ -383,7 +382,7 @@ class AccountInternal extends PureComponent<
// first message referencing a non-deleted row so that we can
// highlight the row
//
let focusId: null | string;
let focusId: null | string = null;
if (
messages.every(msg => msg.dataset === 'transactions') &&
!messages.find(msg => msg.column === 'tombstone')
Expand Down Expand Up @@ -536,7 +535,7 @@ class AccountInternal extends PureComponent<
this.setState(
{
transactions: data,
transactionCount: this.paged?.totalCount,
transactionCount: this.paged?.totalCount ?? 0,
transactionsFiltered: isFiltered,
loading: false,
workingHard: false,
Expand Down Expand Up @@ -678,14 +677,17 @@ class AccountInternal extends PureComponent<
const account = this.props.accounts.find(
account => account.id === accountId,
);
return (
account &&
this.state.search === '' &&
this.state.filterConditions.length === 0 &&
(this.state.sort === null ||
(this.state.sort.field === 'date' &&
this.state.sort.ascDesc === 'desc'))
);

if (!account) return false;
if (this.state.search !== '') return false;
if (this.state.filterConditions.length > 0) return false;
if (this.state.sort === null) {
return true;
} else {
return (
this.state.sort.field === 'date' && this.state.sort.ascDesc === 'desc'
);
}
};

async calculateBalances() {
Expand Down Expand Up @@ -713,7 +715,7 @@ class AccountInternal extends PureComponent<
onSaveName = (name: string) => {
const accountNameError = validateAccountName(
name,
this.props.accountId,
this.props.accountId ?? '',
this.props.accounts,
);
if (accountNameError) {
Expand All @@ -722,7 +724,7 @@ class AccountInternal extends PureComponent<
const account = this.props.accounts.find(
account => account.id === this.props.accountId,
);
this.props.updateAccount({ ...account, name });
this.props.updateAccount({ ...account, name } as AccountEntity);
this.setState({ editingName: false, nameError: '' });
}
};
Expand Down Expand Up @@ -920,9 +922,9 @@ class AccountInternal extends PureComponent<
await this.refetchTransactions();
};

onReconcile = async (balance: number) => {
onReconcile = async (amount: number | null) => {
this.setState(({ showCleared }) => ({
reconcileAmount: balance,
reconcileAmount: amount,
showCleared: true,
prevShowCleared: showCleared,
}));
Expand Down Expand Up @@ -1301,14 +1303,16 @@ class AccountInternal extends PureComponent<

onConditionsOpChange = (value: 'and' | 'or') => {
this.setState({ filterConditionsOp: value });
this.setState({ filterId: { ...this.state.filterId, status: 'changed' } });
this.setState({
filterId: { ...this.state.filterId, status: 'changed' } as SavedFilter,
});
this.applyFilters([...this.state.filterConditions]);
if (this.state.search !== '') {
this.onSearch(this.state.search);
}
};

onReloadSavedFilter = (savedFilter: SavedFilter, item: string) => {
onReloadSavedFilter = (savedFilter: SavedFilter, item?: string) => {
if (item === 'reload') {
const [savedFilter] = this.props.savedFilters.filter(
f => f.id === this.state.filterId?.id,
Expand All @@ -1320,7 +1324,7 @@ class AccountInternal extends PureComponent<
this.setState({
filterConditionsOp: savedFilter.conditionsOp ?? 'and',
});
this.applyFilters([...savedFilter.conditions]);
this.applyFilters([...(savedFilter.conditions ?? [])]);
}
}
this.setState({ filterId: { ...this.state.filterId, ...savedFilter } });
Expand Down Expand Up @@ -1348,7 +1352,7 @@ class AccountInternal extends PureComponent<
filterId: {
...this.state.filterId,
status: this.state.filterId && 'changed',
},
} as SavedFilter,
});
if (this.state.search !== '') {
this.onSearch(this.state.search);
Expand All @@ -1365,7 +1369,7 @@ class AccountInternal extends PureComponent<
filterId: {
...this.state.filterId,
status: this.state.filterId && 'changed',
},
} as SavedFilter,
});
}
if (this.state.search !== '') {
Expand Down Expand Up @@ -1402,7 +1406,7 @@ class AccountInternal extends PureComponent<
filterId: {
...this.state.filterId,
status: this.state.filterId && 'changed',
},
} as SavedFilter,
});
this.applyFilters([...filterConditions, condition]);
}
Expand Down Expand Up @@ -1660,11 +1664,11 @@ class AccountInternal extends PureComponent<

const showEmptyMessage = !loading && !accountId && accounts.length === 0;

const isNameEditable =
accountId &&
accountId !== 'onbudget' &&
accountId !== 'offbudget' &&
accountId !== 'uncategorized';
const isNameEditable = accountId
? accountId !== 'onbudget' &&
accountId !== 'offbudget' &&
accountId !== 'uncategorized'
: false;

const balanceQuery = this.getBalanceQuery(accountId);

Expand All @@ -1687,9 +1691,9 @@ class AccountInternal extends PureComponent<
<View style={styles.page}>
<AccountHeader
tableRef={this.table}
editingName={editingName}
isNameEditable={isNameEditable}
workingHard={workingHard}
editingName={editingName ?? false}
isNameEditable={isNameEditable ?? false}
workingHard={workingHard ?? false}
account={account}
filterId={filterId}
savedFilters={this.props.savedFilters}
Expand All @@ -1698,15 +1702,15 @@ class AccountInternal extends PureComponent<
failedAccounts={failedAccounts}
accounts={accounts}
transactions={transactions}
showBalances={showBalances}
showExtraBalances={showExtraBalances}
showCleared={showCleared}
showReconciled={showReconciled}
showEmptyMessage={showEmptyMessage}
showBalances={showBalances ?? false}
showExtraBalances={showExtraBalances ?? false}
showCleared={showCleared ?? false}
showReconciled={showReconciled ?? false}
showEmptyMessage={showEmptyMessage ?? false}
balanceQuery={balanceQuery}
canCalculateBalance={this.canCalculateBalance}
canCalculateBalance={this?.canCalculateBalance ?? undefined}
filteredAmount={filteredAmount}
isFiltered={transactionsFiltered}
isFiltered={transactionsFiltered ?? false}
isSorted={this.state.sort !== null}
reconcileAmount={reconcileAmount}
search={this.state.search}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { useHover } from 'usehooks-ts';

import { isPreviewId } from 'loot-core/shared/transactions';
import { useCachedSchedules } from 'loot-core/src/client/data-hooks/schedules';
import { q } from 'loot-core/src/shared/query';
import { q, type Query } from 'loot-core/src/shared/query';
import { getScheduledAmount } from 'loot-core/src/shared/schedules';
import { type AccountEntity } from 'loot-core/types/models';

import { useSelectedItems } from '../../hooks/useSelected';
import { SvgArrowButtonRight1 } from '../../icons/v2';
Expand All @@ -15,11 +16,22 @@ import { Button } from '../common/Button2';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { PrivacyFilter } from '../PrivacyFilter';
import { type Binding } from '../spreadsheet';
import { CellValue, CellValueText } from '../spreadsheet/CellValue';
import { useFormat } from '../spreadsheet/useFormat';
import { useSheetValue } from '../spreadsheet/useSheetValue';

function DetailedBalance({ name, balance, isExactBalance = true }) {
type DetailedBalanceProps = {
name: string;
balance: number;
isExactBalance?: boolean;
};

function DetailedBalance({
name,
balance,
isExactBalance = true,
}: DetailedBalanceProps) {
const format = useFormat();
return (
<Text
Expand All @@ -42,32 +54,37 @@ function DetailedBalance({ name, balance, isExactBalance = true }) {
);
}

function SelectedBalance({ selectedItems, account }) {
type SelectedBalanceProps = {
selectedItems: Set<string>;
account?: AccountEntity;
};

function SelectedBalance({ selectedItems, account }: SelectedBalanceProps) {
const { t } = useTranslation();

const name = `selected-balance-${[...selectedItems].join('-')}`;

const rows = useSheetValue({
name,
const rows = useSheetValue<'balance', `selected-transactions-${string}`>({
name: name as `selected-transactions-${string}`,
query: q('transactions')
.filter({
id: { $oneof: [...selectedItems] },
parent_id: { $oneof: [...selectedItems] },
})
.select('id'),
});
const ids = new Set((rows || []).map(r => r.id));
const ids = new Set((rows || []).map((r: { id: string }) => r.id));

const finalIds = [...selectedItems].filter(id => !ids.has(id));
let balance = useSheetValue({
name: name + '-sum',
let balance = useSheetValue<'balance', `selected-balance-${string}`>({
name: (name + '-sum') as `selected-balance-${string}`,
query: q('transactions')
.filter({ id: { $oneof: finalIds } })
.options({ splits: 'all' })
.calculate({ $sum: '$amount' }),
});

let scheduleBalance = null;
let scheduleBalance = 0;

const { isLoading, schedules = [] } = useCachedSchedules();

Expand Down Expand Up @@ -95,14 +112,10 @@ function SelectedBalance({ selectedItems, account }) {
}
}

if (balance == null) {
if (scheduleBalance == null) {
return null;
} else {
balance = scheduleBalance;
}
} else if (scheduleBalance != null) {
balance += scheduleBalance;
if (!balance && !scheduleBalance) {
return null;
} else {
balance = (balance ?? 0) + scheduleBalance;
}

return (
Expand All @@ -114,46 +127,67 @@ function SelectedBalance({ selectedItems, account }) {
);
}

function FilteredBalance({ filteredAmount }) {
type FilteredBalanceProps = {
filteredAmount?: number | null;
};

function FilteredBalance({ filteredAmount }: FilteredBalanceProps) {
const { t } = useTranslation();

return (
<DetailedBalance
name={t('Filtered balance:')}
balance={filteredAmount || 0}
balance={filteredAmount ?? 0}
isExactBalance={true}
/>
);
}

function MoreBalances({ balanceQuery }) {
type MoreBalancesProps = {
balanceQuery: { name: `balance-query-${string}`; query: Query };
};

function MoreBalances({ balanceQuery }: MoreBalancesProps) {
const { t } = useTranslation();

const cleared = useSheetValue({
name: balanceQuery.name + '-cleared',
const cleared = useSheetValue<'balance', `balance-query-${string}-cleared`>({
name: (balanceQuery.name + '-cleared') as `balance-query-${string}-cleared`,
query: balanceQuery.query.filter({ cleared: true }),
});
const uncleared = useSheetValue({
name: balanceQuery.name + '-uncleared',
const uncleared = useSheetValue<
'balance',
`balance-query-${string}-uncleared`
>({
name: (balanceQuery.name +
'-uncleared') as `balance-query-${string}-uncleared`,
query: balanceQuery.query.filter({ cleared: false }),
});

return (
<View style={{ flexDirection: 'row' }}>
<DetailedBalance name={t('Cleared total:')} balance={cleared} />
<DetailedBalance name={t('Uncleared total:')} balance={uncleared} />
<DetailedBalance name={t('Cleared total:')} balance={cleared ?? 0} />
<DetailedBalance name={t('Uncleared total:')} balance={uncleared ?? 0} />
</View>
);
}

type BalancesProps = {
balanceQuery: { name: `balance-query-${string}`; query: Query };
showExtraBalances: boolean;
onToggleExtraBalances: () => void;
account?: AccountEntity;
isFiltered: boolean;
filteredAmount?: number | null;
};

export function Balances({
balanceQuery,
showExtraBalances,
onToggleExtraBalances,
account,
isFiltered,
filteredAmount,
}) {
}: BalancesProps) {
const selectedItems = useSelectedItems();
const buttonRef = useRef(null);
const isButtonHovered = useHover(buttonRef);
Expand All @@ -177,7 +211,15 @@ export function Balances({
paddingBottom: 1,
}}
>
<CellValue binding={{ ...balanceQuery, value: 0 }} type="financial">
<CellValue
binding={
{ ...balanceQuery, value: 0 } as Binding<
'balance',
`balance-query-${string}`
>
}
type="financial"
>
{props => (
<CellValueText
{...props}
Expand Down
Loading

0 comments on commit ce0ca60

Please sign in to comment.