Skip to content

Commit

Permalink
Merge branch 'master' into redux-toolkit-modals-slice
Browse files Browse the repository at this point in the history
  • Loading branch information
joel-jeremy authored Mar 1, 2025
2 parents 7947620 + 8900627 commit e60276b
Show file tree
Hide file tree
Showing 34 changed files with 276 additions and 153 deletions.
1 change: 0 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,6 @@ export default [
'packages/desktop-client/src/components/select/DateSelect.tsx',
'packages/desktop-client/src/components/sidebar/Tools.tsx',
'packages/desktop-client/src/components/sort.tsx',
'packages/desktop-client/src/components/spreadsheet/useSheetValue.ts',
],

rules: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ function TransactionListWithPreviews({
const { t } = useTranslation();
const baseTransactionsQuery = useCallback(
() =>
queries.transactions(accountId).options({ splits: 'none' }).select('*'),
queries.transactions(accountId).options({ splits: 'all' }).select('*'),
[accountId],
);

Expand Down Expand Up @@ -357,7 +357,8 @@ function TransactionListWithPreviews({
);

const transactionsToDisplay = !isSearching
? previewTransactions.concat(transactions)
? // Do not render child transactions in the list, unless searching
previewTransactions.concat(transactions.filter(t => !t.is_child))
: transactions;

return (
Expand Down
13 changes: 11 additions & 2 deletions packages/desktop-client/src/components/spreadsheet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,24 @@ export type SheetNames = keyof Spreadsheets & string;
export type SheetFields<SheetName extends SheetNames> =
keyof Spreadsheets[SheetName] & string;

export type BindingObject<
SheetName extends SheetNames,
SheetFieldName extends SheetFields<SheetName>,
> = {
name: SheetFieldName;
value?: Spreadsheets[SheetName][SheetFieldName] | undefined;
query?: Query | undefined;
};

export type Binding<
SheetName extends SheetNames,
SheetFieldName extends SheetFields<SheetName>,
> =
| SheetFieldName
| {
name: SheetFieldName;
value?: Spreadsheets[SheetName][SheetFieldName];
query?: Query;
value?: Spreadsheets[SheetName][SheetFieldName] | undefined;
query?: Query | undefined;
};
export const parametrizedField =
<SheetName extends SheetNames>() =>
Expand Down
69 changes: 57 additions & 12 deletions packages/desktop-client/src/components/spreadsheet/useSheetValue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useRef, useLayoutEffect, useMemo } from 'react';
import { useState, useRef, useLayoutEffect } from 'react';

import { useSpreadsheet } from 'loot-core/client/SpreadsheetProvider';

Expand All @@ -9,6 +9,7 @@ import {
type SheetFields,
type SheetNames,
type Binding,
type BindingObject,
} from '.';

type SheetValueResult<
Expand All @@ -28,18 +29,18 @@ export function useSheetValue<
): SheetValueResult<SheetName, FieldName>['value'] {
const { sheetName, fullSheetName } = useSheetName(binding);

const bindingObj = useMemo(
const memoizedBinding = useMemoizedBinding(
() =>
typeof binding === 'string'
? { name: binding, value: null, query: undefined }
? { name: binding, value: undefined, query: undefined }
: binding,
[binding],
binding,
);

const spreadsheet = useSpreadsheet();
const [result, setResult] = useState<SheetValueResult<SheetName, FieldName>>({
name: fullSheetName,
value: bindingObj.value ? bindingObj.value : null,
value: memoizedBinding.value ? memoizedBinding.value : null,
});
const latestOnChange = useRef(onChange);
latestOnChange.current = onChange;
Expand All @@ -50,7 +51,7 @@ export function useSheetValue<
useLayoutEffect(() => {
let isMounted = true;

const unbind = spreadsheet.bind(sheetName, bindingObj, newResult => {
const unbind = spreadsheet.bind(sheetName, memoizedBinding, newResult => {
if (!isMounted) {
return;
}
Expand All @@ -74,12 +75,56 @@ export function useSheetValue<
isMounted = false;
unbind();
};
}, [
spreadsheet,
sheetName,
bindingObj.name,
bindingObj.query?.serializeAsString(),
]);
}, [spreadsheet, sheetName, memoizedBinding]);

return result.value;
}

type MemoKey<
SheetName extends SheetNames,
FieldName extends SheetFields<SheetName>,
> = {
name: string;
value?: Spreadsheets[SheetName][FieldName] | undefined;
// We check the serialized query to see if it has changed
serializedQuery?: string;
};

function useMemoizedBinding<
SheetName extends SheetNames,
FieldName extends SheetFields<SheetName>,
>(
memoBinding: () => BindingObject<SheetName, FieldName>,
key: Binding<SheetName, FieldName>,
): BindingObject<SheetName, FieldName> {
const ref = useRef<{
key: MemoKey<SheetName, FieldName>;
value: BindingObject<SheetName, FieldName>;
} | null>(null);

const bindingName = typeof key === 'string' ? key : key.name;
const bindingValue = typeof key === 'string' ? undefined : key.value;
const serializedBindingQuery =
typeof key === 'string' ? undefined : key.query?.serializeAsString();

if (
!ref.current ||
bindingName !== ref.current.key.name ||
bindingValue !== ref.current.key.value ||
serializedBindingQuery !== ref.current.key.serializedQuery
) {
// This should not update the binding reference if the binding name, value, and query values are the same.
// Since query objects are immutable, we compare the serialized query string to make sure that we don't cause
// a re-render whenever a new query object with the same parameter values (QueryState) is passed in.
ref.current = {
key: {
name: bindingName,
value: bindingValue,
serializedQuery: serializedBindingQuery,
},
value: memoBinding(),
};
}

return ref.current.value;
}
7 changes: 2 additions & 5 deletions packages/loot-core/src/client/SpreadsheetProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ export function useSpreadsheet() {
}

// TODO: Make this generic and replace the Binding type in the desktop-client package.
type Binding =
| string
| { name: string; value?: unknown | null; query?: Query | undefined };
type Binding = string | { name: string; query?: Query | undefined };

type CellCacheValue = { name: string; value: Node['value'] | null };
type CellCache = { [name: string]: Promise<CellCacheValue> | null };
Expand Down Expand Up @@ -92,8 +90,7 @@ function makeSpreadsheet() {
binding: Binding,
callback: CellObserverCallback,
): () => void {
binding =
typeof binding === 'string' ? { name: binding, value: null } : binding;
binding = typeof binding === 'string' ? { name: binding } : binding;

if (binding.query) {
this.createQuery(sheetName, binding.name, binding.query);
Expand Down
8 changes: 4 additions & 4 deletions packages/loot-core/src/mocks/budget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,14 +461,14 @@ async function fillOther(handlers, account, payees, groups) {
async function createBudget(accounts, payees, groups) {
const primaryAccount = accounts.find(a => (a.name = 'Bank of America'));
const earliestDate = (
await db.first(
`SELECT * FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
await db.first<Pick<db.DbViewTransaction, 'date'>>(
`SELECT t.date FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
WHERE a.offbudget = 0 AND t.is_child = 0 ORDER BY date ASC LIMIT 1`,
)
).date;
const earliestPrimaryDate = (
await db.first(
`SELECT * FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
await db.first<Pick<db.DbViewTransaction, 'date'>>(
`SELECT t.date FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
WHERE a.id = ? AND a.offbudget = 0 AND t.is_child = 0 ORDER BY date ASC LIMIT 1`,
[primaryAccount.id],
)
Expand Down
Loading

0 comments on commit e60276b

Please sign in to comment.