mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-25 11:37:13 +00:00
feat(plugin-market): show per-format extension counts in type filter
Mirror the LangBot Space marketplace: the advanced-filter type options (plugin / MCP / skill) now display their live extension count, e.g. "插件 (74)". Counts are fetched on mount via three lightweight searchMarketplaceExtensions calls (page_size=1) reading total per type. The all-formats option intentionally shows no count.
This commit is contained in:
@@ -124,6 +124,8 @@ function MarketPageContent({
|
|||||||
const [hasMore, setHasMore] = useState(true);
|
const [hasMore, setHasMore] = useState(true);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
|
// Per-format extension counts shown next to the type filter options.
|
||||||
|
const [typeCounts, setTypeCounts] = useState<Record<string, number>>({});
|
||||||
const [sortOption, setSortOption] = useState<string>(
|
const [sortOption, setSortOption] = useState<string>(
|
||||||
() => loadMarketFilters().sortOption ?? 'install_count_desc',
|
() => loadMarketFilters().sortOption ?? 'install_count_desc',
|
||||||
);
|
);
|
||||||
@@ -320,6 +322,7 @@ function MarketPageContent({
|
|||||||
fetchPlugins(1, false, true);
|
fetchPlugins(1, false, true);
|
||||||
fetchAvailableTags();
|
fetchAvailableTags();
|
||||||
fetchRecommendationLists();
|
fetchRecommendationLists();
|
||||||
|
fetchTypeCounts();
|
||||||
})();
|
})();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
@@ -359,6 +362,32 @@ function MarketPageContent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取各扩展格式的数量(用于筛选器标签上的计数)
|
||||||
|
const fetchTypeCounts = async () => {
|
||||||
|
const types = ['plugin', 'mcp', 'skill'];
|
||||||
|
try {
|
||||||
|
const results = await Promise.all(
|
||||||
|
types.map((type) =>
|
||||||
|
getCloudServiceClientSync()
|
||||||
|
.searchMarketplaceExtensions({
|
||||||
|
page: 1,
|
||||||
|
page_size: 1,
|
||||||
|
type_filter: type,
|
||||||
|
})
|
||||||
|
.then((res) => res.total)
|
||||||
|
.catch(() => 0),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const counts: Record<string, number> = {};
|
||||||
|
types.forEach((type, i) => {
|
||||||
|
counts[type] = results[i];
|
||||||
|
});
|
||||||
|
setTypeCounts(counts);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch extension type counts:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 搜索功能
|
// 搜索功能
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useCallback(
|
||||||
(query: string) => {
|
(query: string) => {
|
||||||
@@ -682,6 +711,7 @@ function MarketPageContent({
|
|||||||
>
|
>
|
||||||
{extensionTypeOptions.map((option) => {
|
{extensionTypeOptions.map((option) => {
|
||||||
const Icon = option.icon;
|
const Icon = option.icon;
|
||||||
|
const count = typeCounts[option.value];
|
||||||
return (
|
return (
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
key={option.value}
|
key={option.value}
|
||||||
@@ -691,6 +721,11 @@ function MarketPageContent({
|
|||||||
>
|
>
|
||||||
{Icon && <Icon className="mr-1 h-3.5 w-3.5" />}
|
{Icon && <Icon className="mr-1 h-3.5 w-3.5" />}
|
||||||
{option.label}
|
{option.label}
|
||||||
|
{typeof count === 'number' && (
|
||||||
|
<span className="ml-1 text-muted-foreground">
|
||||||
|
({count})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user