mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Remove upload functionality and add tooltip for Code not in GitHub link (#7431)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
99aa9bef70
commit
306188817f
@ -24,12 +24,8 @@ import { useGetTrajectory } from "#/hooks/mutation/use-get-trajectory";
|
||||
import { downloadTrajectory } from "#/utils/download-trajectory";
|
||||
import { displayErrorToast } from "#/utils/custom-toast-handlers";
|
||||
|
||||
function getEntryPoint(
|
||||
hasRepository: boolean | null,
|
||||
hasImportedProjectZip: boolean | null,
|
||||
): string {
|
||||
function getEntryPoint(hasRepository: boolean | null): string {
|
||||
if (hasRepository) return "github";
|
||||
if (hasImportedProjectZip) return "zip";
|
||||
return "direct";
|
||||
}
|
||||
|
||||
@ -48,7 +44,7 @@ export function ChatInterface() {
|
||||
>("positive");
|
||||
const [feedbackModalIsOpen, setFeedbackModalIsOpen] = React.useState(false);
|
||||
const [messageToSend, setMessageToSend] = React.useState<string | null>(null);
|
||||
const { selectedRepository, importedProjectZip } = useSelector(
|
||||
const { selectedRepository } = useSelector(
|
||||
(state: RootState) => state.initialQuery,
|
||||
);
|
||||
const params = useParams();
|
||||
@ -57,12 +53,8 @@ export function ChatInterface() {
|
||||
const handleSendMessage = async (content: string, files: File[]) => {
|
||||
if (messages.length === 0) {
|
||||
posthog.capture("initial_query_submitted", {
|
||||
entry_point: getEntryPoint(
|
||||
selectedRepository !== null,
|
||||
importedProjectZip !== null,
|
||||
),
|
||||
entry_point: getEntryPoint(selectedRepository !== null),
|
||||
query_character_length: content.length,
|
||||
uploaded_zip_size: importedProjectZip?.length,
|
||||
});
|
||||
} else {
|
||||
posthog.capture("user_message_sent", {
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useCreateConversation } from "#/hooks/mutation/use-create-conversation";
|
||||
import { setInitialPrompt } from "#/state/initial-query-slice";
|
||||
|
||||
const INITIAL_PROMPT = "";
|
||||
|
||||
export function CodeNotInGitHubLink() {
|
||||
const dispatch = useDispatch();
|
||||
const { mutate: createConversation } = useCreateConversation();
|
||||
|
||||
const handleStartFromScratch = () => {
|
||||
// Set the initial prompt and create a new conversation
|
||||
dispatch(setInitialPrompt(INITIAL_PROMPT));
|
||||
createConversation({ q: INITIAL_PROMPT });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="text-xs text-neutral-400">
|
||||
Code not in GitHub?{" "}
|
||||
<span
|
||||
onClick={handleStartFromScratch}
|
||||
className="underline cursor-pointer"
|
||||
>
|
||||
Start from scratch
|
||||
</span>{" "}
|
||||
and use the VS Code link to upload and download your code.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { SuggestionBox } from "./suggestion-box";
|
||||
|
||||
interface ImportProjectSuggestionBoxProps {
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export function ImportProjectSuggestionBox({
|
||||
onChange,
|
||||
}: ImportProjectSuggestionBoxProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SuggestionBox
|
||||
title={t(I18nKey.LANDING$IMPORT_PROJECT)}
|
||||
content={
|
||||
<label htmlFor="import-project" className="w-full flex justify-center">
|
||||
<span className="border-2 border-dashed border-neutral-600 rounded px-2 py-1 cursor-pointer">
|
||||
{t(I18nKey.LANDING$UPLOAD_ZIP)}
|
||||
</span>
|
||||
<input
|
||||
hidden
|
||||
type="file"
|
||||
accept="application/zip"
|
||||
id="import-project"
|
||||
multiple={false}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</label>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -56,7 +56,7 @@ export function TaskForm({ ref }: TaskFormProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<form
|
||||
ref={ref}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
@ -11,21 +11,12 @@ export const useCreateConversation = () => {
|
||||
const dispatch = useDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { selectedRepository, files, importedProjectZip } = useSelector(
|
||||
const { selectedRepository, files } = useSelector(
|
||||
(state: RootState) => state.initialQuery,
|
||||
);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (variables: { q?: string }) => {
|
||||
if (
|
||||
!variables.q?.trim() &&
|
||||
!selectedRepository &&
|
||||
files.length === 0 &&
|
||||
!importedProjectZip
|
||||
) {
|
||||
throw new Error("No query provided");
|
||||
}
|
||||
|
||||
if (variables.q) dispatch(setInitialPrompt(variables.q));
|
||||
|
||||
return OpenHands.createConversation(
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
import React from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import posthog from "posthog-js";
|
||||
import { setImportedProjectZip } from "#/state/initial-query-slice";
|
||||
import { convertZipToBase64 } from "#/utils/convert-zip-to-base64";
|
||||
import { useGitHubUser } from "#/hooks/query/use-github-user";
|
||||
import { useGitHubAuthUrl } from "#/hooks/use-github-auth-url";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
import { ImportProjectSuggestionBox } from "../../components/features/suggestions/import-project-suggestion-box";
|
||||
import { GitHubRepositoriesSuggestionBox } from "#/components/features/github/github-repositories-suggestion-box";
|
||||
import { CodeNotInGitHubLink } from "#/components/features/github/code-not-in-github-link";
|
||||
import { HeroHeading } from "#/components/shared/hero-heading";
|
||||
import { TaskForm } from "#/components/shared/task-form";
|
||||
|
||||
function Home() {
|
||||
const dispatch = useDispatch();
|
||||
const formRef = React.useRef<HTMLFormElement>(null);
|
||||
|
||||
const { data: config } = useConfig();
|
||||
@ -29,29 +24,20 @@ function Home() {
|
||||
className="bg-base-secondary h-full rounded-xl flex flex-col items-center justify-center relative overflow-y-auto px-2"
|
||||
>
|
||||
<HeroHeading />
|
||||
<div className="flex flex-col gap-8 w-full md:w-[600px] items-center">
|
||||
<div className="flex flex-col gap-1 w-full mt-8 md:w-[600px] items-center">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<TaskForm ref={formRef} />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 w-full flex-col md:flex-row">
|
||||
<div className="flex gap-4 w-full flex-col md:flex-row mt-8">
|
||||
<GitHubRepositoriesSuggestionBox
|
||||
handleSubmit={() => formRef.current?.requestSubmit()}
|
||||
gitHubAuthUrl={gitHubAuthUrl}
|
||||
user={user || null}
|
||||
/>
|
||||
<ImportProjectSuggestionBox
|
||||
onChange={async (event) => {
|
||||
if (event.target.files) {
|
||||
const zip = event.target.files[0];
|
||||
dispatch(setImportedProjectZip(await convertZipToBase64(zip)));
|
||||
posthog.capture("zip_file_uploaded");
|
||||
formRef.current?.requestSubmit();
|
||||
} else {
|
||||
// TODO: handle error
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex justify-start mt-2 ml-2">
|
||||
<CodeNotInGitHubLink />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,44 +1,12 @@
|
||||
import React from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setImportedProjectZip } from "#/state/initial-query-slice";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "#/store";
|
||||
import { base64ToBlob } from "#/utils/base64-to-blob";
|
||||
import { useUploadFiles } from "../../../hooks/mutation/use-upload-files";
|
||||
|
||||
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
|
||||
import { displayErrorToast } from "#/utils/custom-toast-handlers";
|
||||
|
||||
export const useHandleRuntimeActive = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { mutate: uploadFiles } = useUploadFiles();
|
||||
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
||||
|
||||
const runtimeActive = !RUNTIME_INACTIVE_STATES.includes(curAgentState);
|
||||
|
||||
const { importedProjectZip } = useSelector(
|
||||
(state: RootState) => state.initialQuery,
|
||||
);
|
||||
|
||||
const handleUploadFiles = (zip: string) => {
|
||||
const blob = base64ToBlob(zip);
|
||||
const file = new File([blob], "imported-project.zip", {
|
||||
type: blob.type,
|
||||
});
|
||||
uploadFiles(
|
||||
{ files: [file] },
|
||||
{
|
||||
onError: () => {
|
||||
displayErrorToast("Failed to upload project files.");
|
||||
},
|
||||
},
|
||||
);
|
||||
dispatch(setImportedProjectZip(null));
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (runtimeActive && importedProjectZip) {
|
||||
handleUploadFiles(importedProjectZip);
|
||||
}
|
||||
}, [runtimeActive, importedProjectZip]);
|
||||
return { runtimeActive };
|
||||
};
|
||||
|
||||
@ -4,14 +4,12 @@ type SliceState = {
|
||||
files: string[]; // base64 encoded images
|
||||
initialPrompt: string | null;
|
||||
selectedRepository: string | null;
|
||||
importedProjectZip: string | null; // base64 encoded zip
|
||||
};
|
||||
|
||||
const initialState: SliceState = {
|
||||
files: [],
|
||||
initialPrompt: null,
|
||||
selectedRepository: null,
|
||||
importedProjectZip: null,
|
||||
};
|
||||
|
||||
export const selectedFilesSlice = createSlice({
|
||||
@ -39,9 +37,6 @@ export const selectedFilesSlice = createSlice({
|
||||
clearSelectedRepository(state) {
|
||||
state.selectedRepository = null;
|
||||
},
|
||||
setImportedProjectZip(state, action: PayloadAction<string | null>) {
|
||||
state.importedProjectZip = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -53,6 +48,5 @@ export const {
|
||||
clearInitialPrompt,
|
||||
setSelectedRepository,
|
||||
clearSelectedRepository,
|
||||
setImportedProjectZip,
|
||||
} = selectedFilesSlice.actions;
|
||||
export default selectedFilesSlice.reducer;
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
export const convertZipToBase64 = async (file: File) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
return new Promise<string>((resolve) => {
|
||||
reader.onload = () => {
|
||||
resolve(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user