|
|
@@ -8,6 +8,7 @@ import {
|
|
|
useLoaderData,
|
|
|
useFetcher,
|
|
|
Outlet,
|
|
|
+ ClientLoaderFunctionArgs,
|
|
|
} from "@remix-run/react";
|
|
|
import { retrieveGitHubUser, isGitHubErrorReponse } from "#/api/github";
|
|
|
import OpenHands from "#/api/open-hands";
|
|
|
@@ -24,8 +25,11 @@ import { getSettings, settingsAreUpToDate } from "#/services/settings";
|
|
|
import AllHandsLogo from "#/assets/branding/all-hands-logo.svg?react";
|
|
|
import NewProjectIcon from "#/assets/new-project.svg?react";
|
|
|
import DocsIcon from "#/assets/docs.svg?react";
|
|
|
+import { userIsAuthenticated } from "#/utils/user-is-authenticated";
|
|
|
+import { generateGitHubAuthUrl } from "#/utils/generate-github-auth-url";
|
|
|
+import { WaitlistModal } from "#/components/waitlist-modal";
|
|
|
|
|
|
-export const clientLoader = async () => {
|
|
|
+export const clientLoader = async ({ request }: ClientLoaderFunctionArgs) => {
|
|
|
try {
|
|
|
const config = await OpenHands.getConfig();
|
|
|
window.__APP_MODE__ = config.APP_MODE;
|
|
|
@@ -38,6 +42,23 @@ export const clientLoader = async () => {
|
|
|
let token = localStorage.getItem("token");
|
|
|
const ghToken = localStorage.getItem("ghToken");
|
|
|
|
|
|
+ let isAuthed: boolean = false;
|
|
|
+ let githubAuthUrl: string | null = null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ isAuthed = await userIsAuthenticated(ghToken);
|
|
|
+ if (!isAuthed && window.__GITHUB_CLIENT_ID__) {
|
|
|
+ const requestUrl = new URL(request.url);
|
|
|
+ githubAuthUrl = generateGitHubAuthUrl(
|
|
|
+ window.__GITHUB_CLIENT_ID__,
|
|
|
+ requestUrl,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ isAuthed = false;
|
|
|
+ githubAuthUrl = null;
|
|
|
+ }
|
|
|
+
|
|
|
let user: GitHubUser | GitHubErrorReponse | null = null;
|
|
|
if (ghToken) user = await retrieveGitHubUser(ghToken);
|
|
|
|
|
|
@@ -53,6 +74,8 @@ export const clientLoader = async () => {
|
|
|
return defer({
|
|
|
token,
|
|
|
ghToken,
|
|
|
+ isAuthed,
|
|
|
+ githubAuthUrl,
|
|
|
user,
|
|
|
settingsIsUpdated,
|
|
|
settings,
|
|
|
@@ -101,8 +124,15 @@ export default function MainApp() {
|
|
|
const { stop, isConnected } = useSocket();
|
|
|
const navigation = useNavigation();
|
|
|
const location = useLocation();
|
|
|
- const { token, user, settingsIsUpdated, settings } =
|
|
|
- useLoaderData<typeof clientLoader>();
|
|
|
+ const {
|
|
|
+ token,
|
|
|
+ ghToken,
|
|
|
+ user,
|
|
|
+ isAuthed,
|
|
|
+ githubAuthUrl,
|
|
|
+ settingsIsUpdated,
|
|
|
+ settings,
|
|
|
+ } = useLoaderData<typeof clientLoader>();
|
|
|
const logoutFetcher = useFetcher({ key: "logout" });
|
|
|
const endSessionFetcher = useFetcher({ key: "end-session" });
|
|
|
|
|
|
@@ -191,7 +221,7 @@ export default function MainApp() {
|
|
|
type="button"
|
|
|
aria-label="All Hands Logo"
|
|
|
onClick={() => {
|
|
|
- if (location.pathname !== "/")
|
|
|
+ if (location.pathname === "/app")
|
|
|
setStartNewProjectModalIsOpen(true);
|
|
|
}}
|
|
|
>
|
|
|
@@ -239,62 +269,65 @@ export default function MainApp() {
|
|
|
</aside>
|
|
|
<div className="h-full w-full relative">
|
|
|
<Outlet />
|
|
|
- {(!settingsIsUpdated || settingsModalIsOpen) && (
|
|
|
- <ModalBackdrop onClose={() => setSettingsModalIsOpen(false)}>
|
|
|
- <div className="bg-root-primary w-[384px] p-6 rounded-xl flex flex-col gap-2">
|
|
|
- {settingsFormError && (
|
|
|
- <p className="text-danger text-xs">{settingsFormError}</p>
|
|
|
- )}
|
|
|
- <span className="text-xl leading-6 font-semibold -tracking-[0.01em">
|
|
|
- AI Provider Configuration
|
|
|
- </span>
|
|
|
- <p className="text-xs text-[#A3A3A3]">
|
|
|
- To continue, connect an OpenAI, Anthropic, or other LLM account
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {isAuthed && (!settingsIsUpdated || settingsModalIsOpen) && (
|
|
|
+ <ModalBackdrop onClose={() => setSettingsModalIsOpen(false)}>
|
|
|
+ <div className="bg-root-primary w-[384px] p-6 rounded-xl flex flex-col gap-2">
|
|
|
+ {settingsFormError && (
|
|
|
+ <p className="text-danger text-xs">{settingsFormError}</p>
|
|
|
+ )}
|
|
|
+ <span className="text-xl leading-6 font-semibold -tracking-[0.01em">
|
|
|
+ AI Provider Configuration
|
|
|
+ </span>
|
|
|
+ <p className="text-xs text-[#A3A3A3]">
|
|
|
+ To continue, connect an OpenAI, Anthropic, or other LLM account
|
|
|
+ </p>
|
|
|
+ {isConnected && (
|
|
|
+ <p className="text-xs text-danger">
|
|
|
+ Changing settings during an active session will end the session
|
|
|
</p>
|
|
|
- {isConnected && (
|
|
|
- <p className="text-xs text-danger">
|
|
|
- Changing settings during an active session will end the
|
|
|
- session
|
|
|
- </p>
|
|
|
- )}
|
|
|
- <SettingsForm
|
|
|
- settings={settings}
|
|
|
- models={settingsFormData.models}
|
|
|
- agents={settingsFormData.agents}
|
|
|
- securityAnalyzers={settingsFormData.securityAnalyzers}
|
|
|
- onClose={() => setSettingsModalIsOpen(false)}
|
|
|
- />
|
|
|
- </div>
|
|
|
- </ModalBackdrop>
|
|
|
- )}
|
|
|
- {accountSettingsModalOpen && (
|
|
|
- <ModalBackdrop onClose={handleAccountSettingsModalClose}>
|
|
|
- <AccountSettingsModal
|
|
|
- onClose={handleAccountSettingsModalClose}
|
|
|
- selectedLanguage={settings.LANGUAGE}
|
|
|
- gitHubError={isGitHubErrorReponse(user)}
|
|
|
+ )}
|
|
|
+ <SettingsForm
|
|
|
+ settings={settings}
|
|
|
+ models={settingsFormData.models}
|
|
|
+ agents={settingsFormData.agents}
|
|
|
+ securityAnalyzers={settingsFormData.securityAnalyzers}
|
|
|
+ onClose={() => setSettingsModalIsOpen(false)}
|
|
|
/>
|
|
|
- </ModalBackdrop>
|
|
|
- )}
|
|
|
- {startNewProjectModalIsOpen && (
|
|
|
- <ModalBackdrop onClose={() => setStartNewProjectModalIsOpen(false)}>
|
|
|
- <DangerModal
|
|
|
- title="Are you sure you want to exit?"
|
|
|
- description="You will lose any unsaved information."
|
|
|
- buttons={{
|
|
|
- danger: {
|
|
|
- text: "Exit Project",
|
|
|
- onClick: handleEndSession,
|
|
|
- },
|
|
|
- cancel: {
|
|
|
- text: "Cancel",
|
|
|
- onClick: () => setStartNewProjectModalIsOpen(false),
|
|
|
- },
|
|
|
- }}
|
|
|
- />
|
|
|
- </ModalBackdrop>
|
|
|
- )}
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </ModalBackdrop>
|
|
|
+ )}
|
|
|
+ {accountSettingsModalOpen && (
|
|
|
+ <ModalBackdrop onClose={handleAccountSettingsModalClose}>
|
|
|
+ <AccountSettingsModal
|
|
|
+ onClose={handleAccountSettingsModalClose}
|
|
|
+ selectedLanguage={settings.LANGUAGE}
|
|
|
+ gitHubError={isGitHubErrorReponse(user)}
|
|
|
+ />
|
|
|
+ </ModalBackdrop>
|
|
|
+ )}
|
|
|
+ {startNewProjectModalIsOpen && (
|
|
|
+ <ModalBackdrop onClose={() => setStartNewProjectModalIsOpen(false)}>
|
|
|
+ <DangerModal
|
|
|
+ title="Are you sure you want to exit?"
|
|
|
+ description="You will lose any unsaved information."
|
|
|
+ buttons={{
|
|
|
+ danger: {
|
|
|
+ text: "Exit Project",
|
|
|
+ onClick: handleEndSession,
|
|
|
+ },
|
|
|
+ cancel: {
|
|
|
+ text: "Cancel",
|
|
|
+ onClick: () => setStartNewProjectModalIsOpen(false),
|
|
|
+ },
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </ModalBackdrop>
|
|
|
+ )}
|
|
|
+ {!isAuthed && (
|
|
|
+ <WaitlistModal ghToken={ghToken} githubAuthUrl={githubAuthUrl} />
|
|
|
+ )}
|
|
|
</div>
|
|
|
);
|
|
|
}
|