Skip to content

Commit

Permalink
feat(ui): Don't request all spending objects for transactions.
Browse files Browse the repository at this point in the history
Only request the spending objects that are currently in view. This will
increase the overall number of requests but will make the requests
smaller and more efficient. It will also make it easier to support
soft-deleted spending objects in the near future. Which would be
excluded from the list endpoint, but can be requested directly via the
by ID endpoint.
  • Loading branch information
elliotcourant committed Apr 14, 2024
1 parent 193eeb1 commit c72eb81
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 15 deletions.
38 changes: 28 additions & 10 deletions interface/src/components/MSelectSpendingTransaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PriceCheckOutlined, SavingsOutlined } from '@mui/icons-material';
import MBadge from './MBadge';
import MSpan from './MSpan';
import { useCurrentBalance } from '@monetr/interface/hooks/balances';
import { useSpendingSink } from '@monetr/interface/hooks/spending';
import { useSpending, useSpendingSink } from '@monetr/interface/hooks/spending';
import { useUpdateTransaction } from '@monetr/interface/hooks/transactions';
import useTheme from '@monetr/interface/hooks/useTheme';
import Spending, { SpendingType } from '@monetr/interface/models/Spending';
Expand All @@ -20,8 +20,12 @@ export interface MSelectSpendingTransactionProps {
}

export default function MSelectSpendingTransaction(props: MSelectSpendingTransactionProps): JSX.Element {
const [isOpen, setIsOpen] = useState<boolean>(false);
const { transaction } = props;
const { result: allSpending, isLoading: spendingIsLoading } = useSpendingSink();
// Only load all of the spending objects once we actually open a select.
const { result: allSpending, isLoading: spendingIsLoading } = useSpendingSink(isOpen);
// Otherwise use the single specific spending object.
const { data: currentSpending, isLoading: currentSpendingIsLoading } = useSpending(props.transaction.spendingId);
const balances = useCurrentBalance();
const updateTransaction = useUpdateTransaction();
const theme = useTheme();
Expand Down Expand Up @@ -68,19 +72,32 @@ export default function MSelectSpendingTransaction(props: MSelectSpendingTransac
currentAmount: balances?.free,
},
};
const items = allSpending.map(item => ({
label: item.name,
value: item.spendingId,
spending: item,
}));
const items: { [key: number]: { label: string; value: number; spending: Partial<Spending> }} = {};
if (allSpending?.length > 0) {
allSpending.forEach(item => {
items[item.spendingId] = {
label: item.name,
value: item.spendingId,
spending: item,
};
});
}

if (currentSpending) {
items[currentSpending.spendingId] = {
label: currentSpending.name,
value: currentSpending.spendingId,
spending: currentSpending,
};
}

const options = [
freeToUse,
// Labels will be unique. So we only need 1 | -1
...items.sort((a, b) => a.label.toLowerCase() > b.label.toLowerCase() ? 1 : -1),
...Object.values(items).sort((a, b) => a.label.toLowerCase() > b.label.toLowerCase() ? 1 : -1),
];

const selectedItem = !transaction.spendingId ? freeToUse : items.find(item => item.value === transaction.spendingId);
const selectedItem = !transaction.spendingId ? freeToUse : items[transaction.spendingId];

function formatOptionsLabel(option: SpendingOption, meta: FormatOptionLabelMeta<SpendingOption>): React.ReactNode {
if (meta.context === 'value') {
Expand Down Expand Up @@ -130,6 +147,7 @@ export default function MSelectSpendingTransaction(props: MSelectSpendingTransac
components={ {
Option: SpendingSelectOption,
} }
onFocus={ () => setIsOpen(true) }
styles={ {
placeholder: (base: object) => ({
...base,
Expand All @@ -152,7 +170,7 @@ export default function MSelectSpendingTransaction(props: MSelectSpendingTransac
}),
} }
classNamePrefix='m-select-spending-transaction'
isLoading={ isLoading || (spendingIsLoading && Boolean(transaction.spendingId)) }
isLoading={ isLoading || (currentSpendingIsLoading && Boolean(transaction.spendingId)) }
onChange={ handleSpentFromChange }
formatOptionLabel={ formatOptionsLabel }
options={ options }
Expand Down
7 changes: 4 additions & 3 deletions interface/src/hooks/spending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ export type SpendingResult =
{ result: Array<Spending> }
& UseQueryResult<Array<Partial<Spending>>>;

export function useSpendingSink(): SpendingResult {
export function useSpendingSink(enabled: boolean = true): SpendingResult {
const selectedBankAccountId = useSelectedBankAccountId();
console.warn('enabled', enabled);
const result = useQuery<Array<Partial<Spending>>>(
[`/bank_accounts/${ selectedBankAccountId }/spending`],
{
enabled: !!selectedBankAccountId,
enabled: !!selectedBankAccountId && enabled,
},
);
return {
Expand All @@ -28,7 +29,7 @@ export function useSpending(spendingId: number | null): UseQueryResult<Spending>
return useQuery<Partial<Spending>, unknown, Spending>(
[`/bank_accounts/${ selectedBankAccountId }/spending/${spendingId}`],
{
enabled: !!selectedBankAccountId,
enabled: !!selectedBankAccountId && !!spendingId,
select: data => new Spending(data),
},
);
Expand Down
4 changes: 2 additions & 2 deletions interface/src/pages/new/TransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { KeyboardArrowRight } from '@mui/icons-material';

import TransactionMerchantIcon from './TransactionMerchantIcon';
import MSelectSpendingTransaction from '@monetr/interface/components/MSelectSpendingTransaction';
import { useSpendingOld } from '@monetr/interface/hooks/spending';
import { useSpending } from '@monetr/interface/hooks/spending';
import { useAuthentication } from '@monetr/interface/hooks/useAuthentication';
import Transaction from '@monetr/interface/models/Transaction';
import mergeTailwind from '@monetr/interface/util/mergeTailwind';
Expand All @@ -16,7 +16,7 @@ export interface TransactionItemProps {

export default function TransactionItem({ transaction }: TransactionItemProps): JSX.Element {
const user = useAuthentication();
const spending = useSpendingOld(transaction.spendingId);
const spending = useSpending(transaction.spendingId);
const navigate = useNavigate();

const amountClassnames = mergeTailwind(
Expand Down

0 comments on commit c72eb81

Please sign in to comment.