Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/desktop-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
"cross-env": "^10.1.0",
"date-fns": "^4.1.0",
"downshift": "9.3.2",
"fzf": "^0.5.2",
"html-to-image": "^1.11.13",
"hyperformula": "^3.2.0",
"i18next": "^25.10.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import { Text } from '@actual-app/components/text';
import { TextOneLine } from '@actual-app/components/text-one-line';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
import { getNormalisedString } from '@actual-app/core/shared/normalisation';
import { integerToCurrency } from '@actual-app/core/shared/util';
import type {
CategoryEntity,
CategoryGroupEntity,
} from '@actual-app/core/types/models';
import { css, cx } from '@emotion/css';
import { Fzf } from 'fzf';

import { useEnvelopeSheetValue } from '#components/budget/envelope/EnvelopeBudgetComponents';
import { makeAmountFullStyle } from '#components/budget/util';
Expand All @@ -33,8 +33,7 @@ import { useSheetValue } from '#hooks/useSheetValue';
import { useSyncedPref } from '#hooks/useSyncedPref';
import { envelopeBudget, trackingBudget } from '#spreadsheet/bindings';

import { Autocomplete, defaultFilterSuggestion } from './Autocomplete';
import { rankAutocompleteMatch } from './autocompleteRanking';
import { Autocomplete } from './Autocomplete';
import { ItemHeader } from './ItemHeader';

type CategoryAutocompleteItem = Omit<CategoryEntity, 'group'> & {
Expand Down Expand Up @@ -186,22 +185,6 @@ function CategoryList({
);
}

function customSort(obj: CategoryAutocompleteItem, value: string): number {
if (obj.id === 'split') {
return -6;
}
const nameRank = rankAutocompleteMatch(obj.name, value);
if (nameRank < 0) {
return nameRank;
}
// Group name matching: ranks above no-match but below all name tiers.
const groupName = obj.group ? getNormalisedString(obj.group.name) : '';
if (groupName.includes(getNormalisedString(value))) {
return -0.5;
}
return 0;
}

type CategoryAutocompleteProps = ComponentProps<
typeof Autocomplete<CategoryAutocompleteItem>
> & {
Expand Down Expand Up @@ -271,30 +254,22 @@ export function CategoryAutocomplete({
suggestions: CategoryAutocompleteItem[],
value: string,
): CategoryAutocompleteItem[] => {
const normalizedValue = getNormalisedString(value);
return suggestions
.filter(suggestion => {
if (suggestion.id === 'split') {
return true;
}
const splitItem = suggestions.find(s => s.id === 'split');
const realSuggestions = suggestions.filter(s => s.id !== 'split');

if (suggestion.group) {
return (
getNormalisedString(suggestion.group.name).includes(
normalizedValue,
) ||
getNormalisedString(
suggestion.group.name + ' ' + suggestion.name,
).includes(normalizedValue)
);
}
if (!value) {
return suggestions;
}

return defaultFilterSuggestion(suggestion, value);
})
.sort(
(a, b) =>
customSort(a, normalizedValue) - customSort(b, normalizedValue),
);
const filtered = new Fzf(realSuggestions, {
selector: item =>
item.group ? item.group.name + ' ' + item.name : item.name,
limit: 100,
})
.find(value)
.map(result => result.item);

return splitItem ? [splitItem, ...filtered] : filtered;
},
[],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type {
PayeeEntity,
} from '@actual-app/core/types/models';
import { css, cx } from '@emotion/css';
import { Fzf } from 'fzf';

import { useAccounts } from '#hooks/useAccounts';
import { useCommonPayees } from '#hooks/useCommonPayees';
Expand All @@ -41,12 +42,7 @@ import {
useDeletePayeeLocationMutation,
} from '#payees';

import {
Autocomplete,
AutocompleteFooter,
defaultFilterSuggestion,
} from './Autocomplete';
import { rankAutocompleteMatch } from './autocompleteRanking';
import { Autocomplete, AutocompleteFooter } from './Autocomplete';
import { ItemHeader } from './ItemHeader';

type PayeeAutocompleteItem = PayeeEntity &
Expand Down Expand Up @@ -342,13 +338,6 @@ function PayeeList({
);
}

function customSort(obj: PayeeAutocompleteItem, value: string): number {
if (obj.id === 'new') {
return -5;
}
return rankAutocompleteMatch(obj.name, value);
}

export type PayeeAutocompleteProps = ComponentProps<
typeof Autocomplete<PayeeAutocompleteItem>
> & {
Expand Down Expand Up @@ -464,9 +453,12 @@ export function PayeeAutocomplete({
return nearbyPayeesWithType;
}

return nearbyPayeesWithType.filter(payee => {
return defaultFilterSuggestion(payee, rawPayee);
});
return new Fzf(nearbyPayeesWithType, {
selector: item => item.name ?? '',
limit: 100,
})
.find(rawPayee)
.map(result => result.item);
}, [nearbyPayeesWithType, rawPayee]);

const handleForgetLocation = useCallback(
Expand Down Expand Up @@ -506,34 +498,38 @@ export function PayeeAutocomplete({
suggestions: PayeeAutocompleteItem[],
value: string,
) => {
const normalizedValue = getNormalisedString(value);
const filtered = suggestions
.filter(suggestion => {
if (suggestion.id === 'new') {
return !value || value === '' || focusTransferPayees ? false : true;
}
// Separate the 'new' sentinel from real payees
const newItem = suggestions.find(s => s.id === 'new');
const realSuggestions = suggestions.filter(s => s.id !== 'new');

return defaultFilterSuggestion(suggestion, value);
let filtered: PayeeAutocompleteItem[];
if (!value) {
filtered = realSuggestions.slice(0, 100);
} else {
filtered = new Fzf(realSuggestions, {
selector: item => item.name ?? '',
limit: 100,
})
.sort(
(a, b) =>
customSort(a, normalizedValue) - customSort(b, normalizedValue),
)
// Only show the first 100 results, users can search to find more.
// If user want to view all payees, it can be done via the manage payees page.
.slice(0, 100);

if (filtered.length >= 2 && filtered[0].id === 'new') {
const firstFiltered = filtered[1];
.find(value)
.map(result => result.item);
}

// Re-prepend 'new' when the user has typed something
const showNew = newItem && value && value !== '' && !focusTransferPayees;
const results = showNew ? [newItem, ...filtered] : filtered;

if (results.length >= 2 && results[0].id === 'new') {
const firstFiltered = results[1];
if (
getNormalisedString(firstFiltered.name) === normalizedValue &&
getNormalisedString(firstFiltered.name) ===
getNormalisedString(value) &&
!firstFiltered.transfer_acct
) {
// Exact match found, remove the 'Create payee` option.
return filtered.slice(1);
// Exact match found, remove the 'Create payee' option.
return results.slice(1);
}
}
return filtered;
return results;
};

return (
Expand Down

This file was deleted.

This file was deleted.

6 changes: 6 additions & 0 deletions upcoming-release-notes/7261.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: Enhancements
authors: [riid]
---

Improve the search algorithm when selecting a payee or category
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ __metadata:
cross-env: "npm:^10.1.0"
date-fns: "npm:^4.1.0"
downshift: "npm:9.3.2"
fzf: "npm:^0.5.2"
html-to-image: "npm:^1.11.13"
hyperformula: "npm:^3.2.0"
i18next: "npm:^25.10.10"
Expand Down Expand Up @@ -16780,6 +16781,13 @@ __metadata:
languageName: node
linkType: hard

"fzf@npm:^0.5.2":
version: 0.5.2
resolution: "fzf@npm:0.5.2"
checksum: 10/820f9fd068792e792a4c4ab79c719889ee61b2f152089e11a3ba7e9ea081b06bde3b5b6a18131fe4aac61f189640e426a269ccb8b41e946fac9f8c1cc8e98635
languageName: node
linkType: hard

"generator-function@npm:^2.0.0":
version: 2.0.1
resolution: "generator-function@npm:2.0.1"
Expand Down
Loading