From 2d057a73221fdd7025931982fe15263e17bf4fd5 Mon Sep 17 00:00:00 2001 From: bkellam Date: Tue, 10 Feb 2026 18:20:21 -0800 Subject: [PATCH 1/2] s --- .../[owner]/[repo]/components/landingPage.tsx | 60 +++++++++++++++++-- .../[owner]/[repo]/components/loginModal.tsx | 46 ++++++++++++++ .../[domain]/askgh/[owner]/[repo]/page.tsx | 8 ++- 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/loginModal.tsx 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..9a925d66f 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,40 @@ 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); + 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); + } + } + }, [isAuthenticated, editor, createNewChatThread, selectedSearchScopes]); + const imageSrc = imageUrl ? getRepoImageSrc(imageUrl, repoId) : undefined; const displayName = repoDisplayName ?? repoName; @@ -66,9 +113,7 @@ export const LandingPage = ({
{ - createNewChatThread(children, selectedSearchScopes); - }} + onSubmit={handleSubmit} className="min-h-[50px]" isRedirecting={isLoading} languageModels={languageModels} @@ -102,6 +147,13 @@ export const LandingPage = ({ )}
+ + ) } \ No newline at end of file diff --git a/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/loginModal.tsx b/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/loginModal.tsx new file mode 100644 index 000000000..d25223405 --- /dev/null +++ b/packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/loginModal.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { AuthMethodSelector } from "@/app/components/authMethodSelector"; +import type { IdentityProviderMetadata } from "@/lib/identityProviders"; + +interface LoginModalProps { + isOpen: boolean; + onOpenChange: (open: boolean) => void; + providers: IdentityProviderMetadata[]; + callbackUrl: string; +} + +export const LoginModal = ({ + isOpen, + onOpenChange, + providers, + callbackUrl, +}: LoginModalProps) => { + return ( + + + + Sign up to continue + + Sign into your account to continue. + + +
+ +
+
+
+ ); +}; diff --git a/packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx b/packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx index f5c4e98d8..ee1ad7d48 100644 --- a/packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx +++ b/packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx @@ -8,6 +8,8 @@ import { CustomSlateEditor } from "@/features/chat/customSlateEditor"; import { RepoIndexedGuard } from "./components/repoIndexedGuard"; import { LandingPage } from "./components/landingPage"; import { getConfiguredLanguageModelsInfo } from "@/features/chat/actions"; +import { getIdentityProviderMetadata } from "@/lib/identityProviders"; +import { auth } from "@/auth"; interface PageProps { params: Promise<{ owner: string; repo: string }>; @@ -16,7 +18,8 @@ interface PageProps { export default async function GitHubRepoPage(props: PageProps) { const params = await props.params; const { owner, repo } = params; - + const session = await auth(); + const repoId = await (async () => { // 1. Look up repo by owner/repo const displayName = `${owner}/${repo}`; @@ -45,6 +48,7 @@ export default async function GitHubRepoPage(props: PageProps) { const repoInfo = await unwrapServiceError(getRepoInfo(repoId)); const languageModels = await unwrapServiceError(getConfiguredLanguageModelsInfo()); + const providers = getIdentityProviderMetadata(); return ( @@ -55,6 +59,8 @@ export default async function GitHubRepoPage(props: PageProps) { repoDisplayName={repoInfo.displayName ?? undefined} imageUrl={repoInfo.imageUrl ?? undefined} repoId={repoInfo.id} + providers={providers} + isAuthenticated={!!session?.user} /> From 063daa8b9297cadf1c950bec1edb91af1b09ed71 Mon Sep 17 00:00:00 2001 From: bkellam Date: Tue, 10 Feb 2026 18:38:39 -0800 Subject: [PATCH 2/2] feedback --- .../[owner]/[repo]/components/landingPage.tsx | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) 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 9a925d66f..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 @@ -70,18 +70,22 @@ export const LandingPage = ({ if (stored) { hasRestoredPendingMessage.current = true; sessionStorage.removeItem(PENDING_MESSAGE_KEY); - const message = JSON.parse(stored) as Descendant[]; + 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] }); + // 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); + // 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]); @@ -130,7 +134,7 @@ export const LandingPage = ({ repos={[]} searchContexts={[]} selectedSearchScopes={selectedSearchScopes} - onSelectedSearchScopesChange={() => {}} + onSelectedSearchScopesChange={() => { }} isContextSelectorOpen={isContextSelectorOpen} onContextSelectorOpenChanged={setIsContextSelectorOpen} />