mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Small style changes to repo picker (#6013)
Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Graham Neubig <neubig@gmail.com> Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
This commit is contained in:
parent
825a9ba893
commit
761a574b09
@ -14,7 +14,8 @@ describe("GitHubRepositorySelector", () => {
|
||||
<GitHubRepositorySelector
|
||||
onInputChange={onInputChangeMock}
|
||||
onSelect={onSelectMock}
|
||||
repositories={[]}
|
||||
publicRepositories={[]}
|
||||
userRepositories={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
@ -36,7 +37,8 @@ describe("GitHubRepositorySelector", () => {
|
||||
<GitHubRepositorySelector
|
||||
onInputChange={onInputChangeMock}
|
||||
onSelect={onSelectMock}
|
||||
repositories={[]}
|
||||
publicRepositories={[]}
|
||||
userRepositories={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
@ -67,7 +69,8 @@ describe("GitHubRepositorySelector", () => {
|
||||
<GitHubRepositorySelector
|
||||
onInputChange={onInputChangeMock}
|
||||
onSelect={onSelectMock}
|
||||
repositories={[]}
|
||||
publicRepositories={[]}
|
||||
userRepositories={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@ -110,16 +110,11 @@ export const searchPublicRepositories = async (
|
||||
sort: "" | "updated" | "stars" | "forks" = "stars",
|
||||
order: "desc" | "asc" = "desc",
|
||||
): Promise<GitHubRepository[]> => {
|
||||
const sanitizedQuery = query.trim();
|
||||
if (!sanitizedQuery) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const response = await github.get<{ items: GitHubRepository[] }>(
|
||||
"/search/repositories",
|
||||
{
|
||||
params: {
|
||||
q: sanitizedQuery,
|
||||
q: query,
|
||||
per_page,
|
||||
sort,
|
||||
order,
|
||||
|
||||
@ -1,46 +1,48 @@
|
||||
import React from "react";
|
||||
import { Autocomplete, AutocompleteItem } from "@nextui-org/react";
|
||||
import {
|
||||
Autocomplete,
|
||||
AutocompleteItem,
|
||||
AutocompleteSection,
|
||||
} from "@nextui-org/react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import posthog from "posthog-js";
|
||||
import { setSelectedRepository } from "#/state/initial-query-slice";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
|
||||
interface GitHubRepositoryWithPublic extends GitHubRepository {
|
||||
is_public?: boolean;
|
||||
}
|
||||
import { sanitizeQuery } from "#/utils/sanitize-query";
|
||||
|
||||
interface GitHubRepositorySelectorProps {
|
||||
onInputChange: (value: string) => void;
|
||||
onSelect: () => void;
|
||||
repositories: GitHubRepositoryWithPublic[];
|
||||
userRepositories: GitHubRepository[];
|
||||
publicRepositories: GitHubRepository[];
|
||||
}
|
||||
|
||||
export function GitHubRepositorySelector({
|
||||
onInputChange,
|
||||
onSelect,
|
||||
repositories,
|
||||
userRepositories,
|
||||
publicRepositories,
|
||||
}: GitHubRepositorySelectorProps) {
|
||||
const { data: config } = useConfig();
|
||||
const [selectedKey, setSelectedKey] = React.useState<string | null>(null);
|
||||
|
||||
const allRepositories: GitHubRepository[] = [
|
||||
...publicRepositories.filter(
|
||||
(repo) => !publicRepositories.find((r) => r.id === repo.id),
|
||||
),
|
||||
...userRepositories,
|
||||
];
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleRepoSelection = (id: string | null) => {
|
||||
const repo = repositories.find((r) => r.id.toString() === id);
|
||||
if (!repo) return;
|
||||
|
||||
if (repo.id === -1000) {
|
||||
window.open(
|
||||
`https://github.com/apps/${config?.APP_SLUG}/installations/new`,
|
||||
"_blank",
|
||||
);
|
||||
return;
|
||||
const repo = allRepositories.find((r) => r.id.toString() === id);
|
||||
if (repo) {
|
||||
dispatch(setSelectedRepository(repo.full_name));
|
||||
posthog.capture("repository_selected");
|
||||
onSelect();
|
||||
setSelectedKey(id);
|
||||
}
|
||||
|
||||
dispatch(setSelectedRepository(repo.full_name));
|
||||
posthog.capture("repository_selected");
|
||||
onSelect();
|
||||
setSelectedKey(id);
|
||||
};
|
||||
|
||||
const handleClearSelection = () => {
|
||||
@ -55,8 +57,8 @@ export function GitHubRepositorySelector({
|
||||
name="repo"
|
||||
aria-label="GitHub Repository"
|
||||
placeholder="Select a GitHub project"
|
||||
isVirtualized={false}
|
||||
selectedKey={selectedKey}
|
||||
items={repositories}
|
||||
inputProps={{
|
||||
classNames: {
|
||||
inputWrapper:
|
||||
@ -65,27 +67,61 @@ export function GitHubRepositorySelector({
|
||||
}}
|
||||
onSelectionChange={(id) => handleRepoSelection(id?.toString() ?? null)}
|
||||
onInputChange={onInputChange}
|
||||
clearButtonProps={{ onPress: handleClearSelection }}
|
||||
clearButtonProps={{ onClick: handleClearSelection }}
|
||||
listboxProps={{
|
||||
emptyContent,
|
||||
}}
|
||||
defaultFilter={(textValue, inputValue) =>
|
||||
!inputValue ||
|
||||
sanitizeQuery(textValue).includes(sanitizeQuery(inputValue))
|
||||
}
|
||||
>
|
||||
{(item) => (
|
||||
<AutocompleteItem
|
||||
data-testid="github-repo-item"
|
||||
key={item.id}
|
||||
value={item.id}
|
||||
textValue={item.full_name}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
{item.full_name}
|
||||
{item.is_public && !!item.stargazers_count && (
|
||||
<span className="text-xs text-gray-400">
|
||||
({item.stargazers_count}⭐)
|
||||
{config?.APP_MODE === "saas" &&
|
||||
config?.APP_SLUG &&
|
||||
((
|
||||
<AutocompleteItem key="install">
|
||||
<a
|
||||
href={`https://github.com/apps/${config.APP_SLUG}/installations/new`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
Add more repositories...
|
||||
</a>
|
||||
</AutocompleteItem> // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
) as any)}
|
||||
{userRepositories.length > 0 && (
|
||||
<AutocompleteSection showDivider title="Your Repos">
|
||||
{userRepositories.map((repo) => (
|
||||
<AutocompleteItem
|
||||
data-testid="github-repo-item"
|
||||
key={repo.id}
|
||||
value={repo.id}
|
||||
className="data-[selected=true]:bg-default-100"
|
||||
textValue={repo.full_name}
|
||||
>
|
||||
{repo.full_name}
|
||||
</AutocompleteItem>
|
||||
))}
|
||||
</AutocompleteSection>
|
||||
)}
|
||||
{publicRepositories.length > 0 && (
|
||||
<AutocompleteSection showDivider title="Public Repos">
|
||||
{publicRepositories.map((repo) => (
|
||||
<AutocompleteItem
|
||||
data-testid="github-repo-item"
|
||||
key={repo.id}
|
||||
value={repo.id}
|
||||
className="data-[selected=true]:bg-default-100"
|
||||
textValue={repo.full_name}
|
||||
>
|
||||
{repo.full_name}
|
||||
<span className="ml-1 text-gray-400">
|
||||
({repo.stargazers_count || 0}⭐)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</AutocompleteItem>
|
||||
</AutocompleteItem>
|
||||
))}
|
||||
</AutocompleteSection>
|
||||
)}
|
||||
</Autocomplete>
|
||||
);
|
||||
|
||||
@ -11,7 +11,6 @@ import { useSearchRepositories } from "#/hooks/query/use-search-repositories";
|
||||
import { useUserRepositories } from "#/hooks/query/use-user-repositories";
|
||||
import { sanitizeQuery } from "#/utils/sanitize-query";
|
||||
import { useDebounce } from "#/hooks/use-debounce";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
|
||||
interface GitHubRepositoriesSuggestionBoxProps {
|
||||
handleSubmit: () => void;
|
||||
@ -29,7 +28,6 @@ export function GitHubRepositoriesSuggestionBox({
|
||||
const [searchQuery, setSearchQuery] = React.useState<string>("");
|
||||
const debouncedSearchQuery = useDebounce(searchQuery, 300);
|
||||
|
||||
const { data: config } = useConfig();
|
||||
// TODO: Use `useQueries` to fetch all repositories in parallel
|
||||
const { data: appRepositories } = useAppRepositories();
|
||||
const { data: userRepositories } = useUserRepositories();
|
||||
@ -37,19 +35,6 @@ export function GitHubRepositoriesSuggestionBox({
|
||||
sanitizeQuery(debouncedSearchQuery),
|
||||
);
|
||||
|
||||
const saasPlaceholderRepository = React.useMemo(() => {
|
||||
if (config?.APP_MODE === "saas" && config?.APP_SLUG) {
|
||||
return [
|
||||
{
|
||||
id: -1000,
|
||||
full_name: "Add more repositories...",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}, [config]);
|
||||
|
||||
const repositories =
|
||||
userRepositories?.pages.flatMap((page) => page.data) ||
|
||||
appRepositories?.pages.flatMap((page) => page.data) ||
|
||||
@ -74,11 +59,8 @@ export function GitHubRepositoriesSuggestionBox({
|
||||
<GitHubRepositorySelector
|
||||
onInputChange={setSearchQuery}
|
||||
onSelect={handleSubmit}
|
||||
repositories={[
|
||||
...saasPlaceholderRepository,
|
||||
...searchedRepos,
|
||||
...repositories,
|
||||
]}
|
||||
publicRepositories={searchedRepos}
|
||||
userRepositories={repositories}
|
||||
/>
|
||||
) : (
|
||||
<ModalButton
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export const sanitizeQuery = (query: string) =>
|
||||
query
|
||||
.trim()
|
||||
.replace(/https?:\/\//, "")
|
||||
.replace(/github.com\//, "")
|
||||
.replace(/\.git$/, "")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user