fix: library search boosting

Closes #1106

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta
2023-02-07 13:19:55 +01:00
committed by Akos Kitta
parent 5d264ef5b6
commit 79b6b7ecc0
14 changed files with 627 additions and 346 deletions

View File

@@ -30,7 +30,6 @@ export class BoardsListWidget extends ListWidget<BoardsPackage, BoardSearch> {
searchable: service,
installable: service,
itemLabel: (item: BoardsPackage) => item.name,
itemDeprecated: (item: BoardsPackage) => item.deprecated,
itemRenderer,
filterRenderer,
defaultSearchOptions: { query: '', type: 'All' },

View File

@@ -41,7 +41,6 @@ export class LibraryListWidget extends ListWidget<
searchable: service,
installable: service,
itemLabel: (item: LibraryPackage) => item.name,
itemDeprecated: (item: LibraryPackage) => item.deprecated,
itemRenderer,
filterRenderer,
defaultSearchOptions: { query: '', type: 'All', topic: 'All' },

View File

@@ -147,7 +147,6 @@ export namespace ComponentList {
export interface Props<T extends ArduinoComponent> {
readonly items: T[];
readonly itemLabel: (item: T) => string;
readonly itemDeprecated: (item: T) => boolean;
readonly itemRenderer: ListItemRenderer<T>;
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
readonly uninstall: (item: T) => Promise<void>;

View File

@@ -82,12 +82,11 @@ export class FilterableListContainer<
}
protected renderComponentList(): React.ReactNode {
const { itemLabel, itemDeprecated, itemRenderer } = this.props;
const { itemLabel, itemRenderer } = this.props;
return (
<ComponentList<T>
items={this.state.items}
itemLabel={itemLabel}
itemDeprecated={itemDeprecated}
itemRenderer={itemRenderer}
install={this.install.bind(this)}
uninstall={this.uninstall.bind(this)}
@@ -109,9 +108,7 @@ export class FilterableListContainer<
protected search(searchOptions: S): void {
const { searchable } = this.props;
searchable
.search(searchOptions)
.then((items) => this.setState({ items: this.props.sort(items) }));
searchable.search(searchOptions).then((items) => this.setState({ items }));
}
protected async install(
@@ -127,7 +124,7 @@ export class FilterableListContainer<
run: ({ progressId }) => install({ item, progressId, version }),
});
const items = await searchable.search(this.state.searchOptions);
this.setState({ items: this.props.sort(items) });
this.setState({ items });
}
protected async uninstall(item: T): Promise<void> {
@@ -155,7 +152,7 @@ export class FilterableListContainer<
run: ({ progressId }) => uninstall({ item, progressId }),
});
const items = await searchable.search(this.state.searchOptions);
this.setState({ items: this.props.sort(items) });
this.setState({ items });
}
}
@@ -168,7 +165,6 @@ export namespace FilterableListContainer {
readonly container: ListWidget<T, S>;
readonly searchable: Searchable<T, S>;
readonly itemLabel: (item: T) => string;
readonly itemDeprecated: (item: T) => boolean;
readonly itemRenderer: ListItemRenderer<T>;
readonly filterRenderer: FilterRenderer<S>;
readonly resolveFocus: (element: HTMLElement | undefined) => void;
@@ -192,7 +188,6 @@ export namespace FilterableListContainer {
progressId: string;
}) => Promise<void>;
readonly commandService: CommandService;
readonly sort: (items: T[]) => T[];
}
export interface State<T, S extends Searchable.Options> {

View File

@@ -53,11 +53,9 @@ export abstract class ListWidget<
*/
protected firstActivate = true;
protected readonly defaultSortComparator: (left: T, right: T) => number;
constructor(protected options: ListWidget.Options<T, S>) {
super();
const { id, label, iconClass, itemDeprecated, itemLabel } = options;
const { id, label, iconClass } = options;
this.id = id;
this.title.label = label;
this.title.caption = label;
@@ -67,15 +65,6 @@ export abstract class ListWidget<
this.node.tabIndex = 0; // To be able to set the focus on the widget.
this.scrollOptions = undefined;
this.toDispose.push(this.searchOptionsChangeEmitter);
this.defaultSortComparator = (left, right): number => {
// always put deprecated items at the bottom of the list
if (itemDeprecated(left)) {
return 1;
}
return itemLabel(left).localeCompare(itemLabel(right));
};
}
@postConstruct()
@@ -144,30 +133,6 @@ export abstract class ListWidget<
return this.options.installable.uninstall({ item, progressId });
}
protected filterableListSort = (items: T[]): T[] => {
const isArduinoTypeComparator = (left: T, right: T) => {
const aIsArduinoType = left.types.includes('Arduino');
const bIsArduinoType = right.types.includes('Arduino');
if (aIsArduinoType && !bIsArduinoType && !left.deprecated) {
return -1;
}
if (!aIsArduinoType && bIsArduinoType && !right.deprecated) {
return 1;
}
return 0;
};
return items.sort((left, right) => {
return (
isArduinoTypeComparator(left, right) ||
this.defaultSortComparator(left, right)
);
});
};
render(): React.ReactNode {
return (
<FilterableListContainer<T, S>
@@ -178,14 +143,12 @@ export abstract class ListWidget<
install={this.install.bind(this)}
uninstall={this.uninstall.bind(this)}
itemLabel={this.options.itemLabel}
itemDeprecated={this.options.itemDeprecated}
itemRenderer={this.options.itemRenderer}
filterRenderer={this.options.filterRenderer}
searchOptionsDidChange={this.searchOptionsChangeEmitter.event}
messageService={this.messageService}
commandService={this.commandService}
responseService={this.responseService}
sort={this.filterableListSort}
/>
);
}
@@ -218,7 +181,6 @@ export namespace ListWidget {
readonly installable: Installable<T>;
readonly searchable: Searchable<T, S>;
readonly itemLabel: (item: T) => string;
readonly itemDeprecated: (item: T) => boolean;
readonly itemRenderer: ListItemRenderer<T>;
readonly filterRenderer: FilterRenderer<S>;
readonly defaultSearchOptions: S;