mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 17:38:35 +00:00
perf(webui): make marketplace filters instant
This commit is contained in:
parent
12ebc7b8e2
commit
d276fe1386
@ -53,11 +53,25 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
() => new Set(installedSkills.map((skill) => skill.name)),
|
() => new Set(installedSkills.map((skill) => skill.name)),
|
||||||
[installedSkills],
|
[installedSkills],
|
||||||
);
|
);
|
||||||
|
const visibleTrending = useMemo(
|
||||||
|
() =>
|
||||||
|
provider === "all"
|
||||||
|
? trending
|
||||||
|
: trending.filter((skill) => skill.provider === provider),
|
||||||
|
[provider, trending],
|
||||||
|
);
|
||||||
|
const visibleResults = useMemo(
|
||||||
|
() =>
|
||||||
|
provider === "all"
|
||||||
|
? results
|
||||||
|
: results.filter((skill) => skill.provider === provider),
|
||||||
|
[provider, results],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setTrendingLoading(true);
|
setTrendingLoading(true);
|
||||||
fetchTrendingMarketplaceSkills(token, provider)
|
fetchTrendingMarketplaceSkills(token)
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
setTrending(payload.skills);
|
setTrending(payload.skills);
|
||||||
@ -71,7 +85,7 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [provider, token]);
|
}, [token]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const skills = query.trim().length < 2 ? trending : results;
|
const skills = query.trim().length < 2 ? trending : results;
|
||||||
@ -106,7 +120,7 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
const timer = window.setTimeout(() => {
|
const timer = window.setTimeout(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
searchMarketplaceSkills(token, normalized, provider)
|
searchMarketplaceSkills(token, normalized)
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
setResults(payload.skills);
|
setResults(payload.skills);
|
||||||
@ -118,7 +132,7 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
reason instanceof Error
|
reason instanceof Error
|
||||||
? reason.message
|
? reason.message
|
||||||
: t("settings.skills.marketplaceSearchFailed", {
|
: t("settings.skills.marketplaceSearchFailed", {
|
||||||
defaultValue: "Could not search skills.sh.",
|
defaultValue: "Could not search skill marketplaces.",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -131,7 +145,7 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
cancelled = true;
|
cancelled = true;
|
||||||
window.clearTimeout(timer);
|
window.clearTimeout(timer);
|
||||||
};
|
};
|
||||||
}, [provider, query, t, token]);
|
}, [query, t, token]);
|
||||||
|
|
||||||
const install = async (skill: MarketplaceSkillSummary) => {
|
const install = async (skill: MarketplaceSkillSummary) => {
|
||||||
setSelected(null);
|
setSelected(null);
|
||||||
@ -238,9 +252,9 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
</div>
|
</div>
|
||||||
{trendingLoading ? (
|
{trendingLoading ? (
|
||||||
<TrendingSkeleton />
|
<TrendingSkeleton />
|
||||||
) : trending.length ? (
|
) : visibleTrending.length ? (
|
||||||
<MarketplaceSkillGroups
|
<MarketplaceSkillGroups
|
||||||
skills={trending}
|
skills={visibleTrending}
|
||||||
installedNames={installedNames}
|
installedNames={installedNames}
|
||||||
installing={installing}
|
installing={installing}
|
||||||
trends={trends}
|
trends={trends}
|
||||||
@ -255,7 +269,7 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
) : !loading && results.length === 0 && !error ? (
|
) : !loading && visibleResults.length === 0 && !error ? (
|
||||||
<div className="rounded-[22px] bg-settings-surface px-5 py-12 text-center text-sm text-muted-foreground">
|
<div className="rounded-[22px] bg-settings-surface px-5 py-12 text-center text-sm text-muted-foreground">
|
||||||
{t("settings.skills.marketplaceEmpty", {
|
{t("settings.skills.marketplaceEmpty", {
|
||||||
query: query.trim(),
|
query: query.trim(),
|
||||||
@ -265,7 +279,7 @@ export function SkillsMarketplace({ installedSkills }: { installedSkills: SkillS
|
|||||||
) : (
|
) : (
|
||||||
<div className="overflow-hidden rounded-[22px] bg-settings-surface">
|
<div className="overflow-hidden rounded-[22px] bg-settings-surface">
|
||||||
<MarketplaceSkillGroups
|
<MarketplaceSkillGroups
|
||||||
skills={results}
|
skills={visibleResults}
|
||||||
installedNames={installedNames}
|
installedNames={installedNames}
|
||||||
installing={installing}
|
installing={installing}
|
||||||
trends={trends}
|
trends={trends}
|
||||||
|
|||||||
@ -717,6 +717,18 @@ describe("App layout", () => {
|
|||||||
expect(screen.getAllByText("SkillHub")).toHaveLength(2);
|
expect(screen.getAllByText("SkillHub")).toHaveLength(2);
|
||||||
expect(screen.getAllByText("skills.sh")).toHaveLength(2);
|
expect(screen.getAllByText("skills.sh")).toHaveLength(2);
|
||||||
expect(screen.getByText(/14,481 installs \/ 24h/)).toBeInTheDocument();
|
expect(screen.getByText(/14,481 installs \/ 24h/)).toBeInTheDocument();
|
||||||
|
fireEvent.click(screen.getByRole("tab", { name: "SkillHub" }));
|
||||||
|
expect(screen.getByText("ima-skills")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("find-skills")).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
vi.mocked(fetch).mock.calls.some(
|
||||||
|
([input]) =>
|
||||||
|
String(input) === "/api/webui/skills/trending?provider=skillhub",
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
fireEvent.click(screen.getByRole("tab", { name: "All" }));
|
||||||
|
expect(screen.getByText("find-skills")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("ima-skills")).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
await screen.findByRole("img", { name: "8-week install trend" }),
|
await screen.findByRole("img", { name: "8-week install trend" }),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user