From 4f954350997323f668fb1625c043eae8339f6b56 Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 26 Dec 2024 13:17:22 +0000 Subject: [PATCH] Fix pr #5783: feat(frontend): enhance GitHub repo picker with search and sorting --- .../features/github/github-repo-selector.tsx | 46 +++++++++++-------- frontend/src/hooks/use-debounce.ts | 12 +++++ 2 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 frontend/src/hooks/use-debounce.ts diff --git a/frontend/src/components/features/github/github-repo-selector.tsx b/frontend/src/components/features/github/github-repo-selector.tsx index bbcc9702ac3f..2e6eb2c4daba 100644 --- a/frontend/src/components/features/github/github-repo-selector.tsx +++ b/frontend/src/components/features/github/github-repo-selector.tsx @@ -5,10 +5,15 @@ import posthog from "posthog-js"; import { setSelectedRepository } from "#/state/initial-query-slice"; import { useConfig } from "#/hooks/query/use-config"; import { searchPublicRepositories } from "#/api/github"; +import { useDebounce } from "#/hooks/use-debounce"; + +interface GitHubRepositoryWithFlag extends GitHubRepository { + fromPublicRepoSearch?: boolean; +} interface GitHubRepositorySelectorProps { onSelect: () => void; - repositories: GitHubRepository[]; + repositories: GitHubRepositoryWithFlag[]; } export function GitHubRepositorySelector({ @@ -18,29 +23,34 @@ export function GitHubRepositorySelector({ const { data: config } = useConfig(); const [selectedKey, setSelectedKey] = React.useState(null); const [searchQuery, setSearchQuery] = React.useState(""); - const [searchedRepos, setSearchedRepos] = React.useState( - [], - ); + const [searchedRepos, setSearchedRepos] = React.useState([]); + const debouncedSearchQuery = useDebounce(searchQuery, 300); React.useEffect(() => { const searchPublicRepo = async () => { - const repos = await searchPublicRepositories(searchQuery); - setSearchedRepos(repos); + if (!debouncedSearchQuery) { + setSearchedRepos([]); + return; + } + const repos = await searchPublicRepositories(debouncedSearchQuery); + // Sort by stars in descending order + const sortedRepos = repos + .sort((a, b) => (b.stargazers_count || 0) - (a.stargazers_count || 0)) + .slice(0, 5) // Take top 5 results + .map(repo => ({ + ...repo, + fromPublicRepoSearch: true, + })); + setSearchedRepos(sortedRepos); }; - const debounceTimeout = setTimeout(searchPublicRepo, 300); - return () => clearTimeout(debounceTimeout); - }, [searchQuery]); + searchPublicRepo(); + }, [debouncedSearchQuery]); - const finalRepositories = repositories; - searchedRepos.forEach((repo) => { - if (!repositories.find((r) => r.id === repo.id)) { - finalRepositories.unshift({ - ...repo, - fromPublicRepoSearch: true, - }); - } - }); + const finalRepositories: GitHubRepositoryWithFlag[] = [ + ...searchedRepos.filter(repo => !repositories.find(r => r.id === repo.id)), + ...repositories, + ]; const dispatch = useDispatch(); diff --git a/frontend/src/hooks/use-debounce.ts b/frontend/src/hooks/use-debounce.ts new file mode 100644 index 000000000000..d39df3f066fc --- /dev/null +++ b/frontend/src/hooks/use-debounce.ts @@ -0,0 +1,12 @@ +import { useEffect, useState } from "react"; + +export function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const timer = setTimeout(() => setDebouncedValue(value), delay); + return () => clearTimeout(timer); + }, [value, delay]); + + return debouncedValue; +}