diff --git a/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx b/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx index 98d14eed6..9e29dd46c 100644 --- a/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx +++ b/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx @@ -5,11 +5,17 @@ import { SearchModeSelector } from "@/app/[domain]/components/searchModeSelector import { Separator } from "@/components/ui/separator"; import { ChatBox } from "@/features/chat/components/chatBox"; import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolbar"; +import { LoginModal } from "./loginModal"; import { NotConfiguredErrorBanner } from "@/features/chat/components/notConfiguredErrorBanner"; import { LanguageModelInfo, RepoSearchScope } from "@/features/chat/types"; import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread"; import { getRepoImageSrc } from '@/lib/utils'; -import { useMemo, useState } from "react"; +import type { IdentityProviderMetadata } from "@/lib/identityProviders"; +import { Descendant, Transforms } from "slate"; +import { useSlate } from "slate-react"; +import { useCallback, useEffect, useMemo, useState, useRef } from "react"; + +const PENDING_MESSAGE_KEY = "askgh_pending_message"; interface LandingPageProps { languageModels: LanguageModelInfo[]; @@ -17,6 +23,8 @@ interface LandingPageProps { repoDisplayName?: string; imageUrl?: string | null; repoId: number; + providers: IdentityProviderMetadata[]; + isAuthenticated: boolean; } export const LandingPage = ({ @@ -25,9 +33,14 @@ export const LandingPage = ({ repoDisplayName, imageUrl, repoId, + providers, + isAuthenticated, }: LandingPageProps) => { + const editor = useSlate(); const { createNewChatThread, isLoading } = useCreateNewChatThread(); const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false); + const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); + const hasRestoredPendingMessage = useRef(false); const isChatBoxDisabled = languageModels.length === 0; const selectedSearchScopes = useMemo(() => [ @@ -39,6 +52,44 @@ export const LandingPage = ({ } satisfies RepoSearchScope, ], [repoDisplayName, repoName]); + // Intercept submit to check auth status + const handleSubmit = useCallback((children: Descendant[]) => { + if (!isAuthenticated) { + // Store message in sessionStorage to survive OAuth redirect + sessionStorage.setItem(PENDING_MESSAGE_KEY, JSON.stringify(children)); + setIsLoginModalOpen(true); + return; + } + createNewChatThread(children, selectedSearchScopes); + }, [isAuthenticated, createNewChatThread, selectedSearchScopes]); + + // Restore pending message to editor and auto-submit after login + useEffect(() => { + if (isAuthenticated && !hasRestoredPendingMessage.current) { + const stored = sessionStorage.getItem(PENDING_MESSAGE_KEY); + if (stored) { + hasRestoredPendingMessage.current = true; + sessionStorage.removeItem(PENDING_MESSAGE_KEY); + try { + const message = JSON.parse(stored) as Descendant[]; + + // Restore the message content to the editor by replacing all nodes + // Remove all existing nodes + while (editor.children.length > 0) { + Transforms.removeNodes(editor, { at: [0] }); + } + // Insert the restored content at the beginning + Transforms.insertNodes(editor, message, { at: [0] }); + + // Allow the UI to render the restored text before auto-submitting + createNewChatThread(message, selectedSearchScopes); + } catch (error) { + console.error('Failed to restore pending message:', error); + } + } + } + }, [isAuthenticated, editor, createNewChatThread, selectedSearchScopes]); + const imageSrc = imageUrl ? getRepoImageSrc(imageUrl, repoId) : undefined; const displayName = repoDisplayName ?? repoName; @@ -66,9 +117,7 @@ export const LandingPage = ({