diff --git a/CHANGELOG.md b/CHANGELOG.md index df792a756..45f4d08c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Added login wall for code search for Ask GitHub. [#1680](https://github.com/sourcebot-dev/sourcebot/pull/1680) + ### Removed - Removed the Ask Sourcebot first-visit tutorial banner. [#1675](https://github.com/sourcebot-dev/sourcebot/pull/1675) - Removed suggested example queries from the Ask landing page. [#1674](https://github.com/sourcebot-dev/sourcebot/pull/1674) diff --git a/packages/web/src/app/(app)/browse/layout.tsx b/packages/web/src/app/(app)/browse/layout.tsx index d9b555c0a..084d1e96f 100644 --- a/packages/web/src/app/(app)/browse/layout.tsx +++ b/packages/web/src/app/(app)/browse/layout.tsx @@ -1,5 +1,7 @@ import { LayoutClient } from "./layoutClient"; import { getConfiguredLanguageModelsInfo } from "@/features/chat/utils.server"; +import { auth } from "@/auth"; +import { env } from "@sourcebot/shared"; interface LayoutProps { children: React.ReactNode; @@ -8,9 +10,15 @@ interface LayoutProps { export default async function Layout({ children, }: LayoutProps) { - const languageModels = await getConfiguredLanguageModelsInfo(); + const [languageModels, session] = await Promise.all([ + getConfiguredLanguageModelsInfo(), + auth(), + ]); return ( - 0}> + 0} + showLoginWall={env.EXPERIMENT_ASK_GH_ENABLED === "true" && !session?.user} + > {children} ) diff --git a/packages/web/src/app/(app)/browse/layoutClient.tsx b/packages/web/src/app/(app)/browse/layoutClient.tsx index fb0922d6e..60e36a6ce 100644 --- a/packages/web/src/app/(app)/browse/layoutClient.tsx +++ b/packages/web/src/app/(app)/browse/layoutClient.tsx @@ -14,11 +14,13 @@ import { Separator } from "@/components/ui/separator"; interface LayoutProps { children: React.ReactNode; isSearchAssistSupported: boolean; + showLoginWall: boolean; } export function LayoutClient({ children, isSearchAssistSupported, + showLoginWall, }: LayoutProps) { const { repoName, revisionName, pathType } = useBrowseParams(); return ( @@ -33,6 +35,7 @@ export function LayoutClient({ }} className="w-full" isSearchAssistSupported={isSearchAssistSupported} + showLoginWall={showLoginWall} /> diff --git a/packages/web/src/app/(app)/components/searchBar/searchBar.tsx b/packages/web/src/app/(app)/components/searchBar/searchBar.tsx index 7ce3a537c..af75f7a98 100644 --- a/packages/web/src/app/(app)/components/searchBar/searchBar.tsx +++ b/packages/web/src/app/(app)/components/searchBar/searchBar.tsx @@ -47,6 +47,7 @@ import Link from "next/link"; import { CaseSensitiveIcon, RegexIcon, Wand2Icon } from "lucide-react"; import { SearchAssistBox } from "./searchAssistBox"; import useCaptureEvent from "@/hooks/useCaptureEvent"; +import { LoginDialog } from "@/app/components/loginDialog"; const LANGUAGE_MODEL_DOCS_URL = "https://docs.sourcebot.dev/docs/configuration/language-model-providers"; @@ -60,6 +61,7 @@ interface SearchBarProps { } autoFocus?: boolean; isSearchAssistSupported: boolean; + showLoginWall: boolean; } const searchBarKeymap: readonly KeyBinding[] = ([ @@ -107,6 +109,7 @@ export const SearchBar = ({ query: defaultQuery = "", } = {}, isSearchAssistSupported, + showLoginWall, }: SearchBarProps) => { const router = useRouter(); const captureEvent = useCaptureEvent(); @@ -120,6 +123,7 @@ export const SearchBar = ({ const [isHistorySearchEnabled, setIsHistorySearchEnabled] = useState(false); const [isRegexEnabled, setIsRegexEnabled] = useState(defaultIsRegexEnabled); const [isCaseSensitivityEnabled, setIsCaseSensitivityEnabled] = useState(defaultIsCaseSensitivityEnabled); + const [loginCallbackUrl, setLoginCallbackUrl] = useState(); const focusEditor = useCallback(() => editorRef.current?.view?.focus(), []); const focusSuggestionsBox = useCallback(() => suggestionBoxRef.current?.focus(), []); @@ -230,8 +234,24 @@ export const SearchBar = ({ [SearchQueryParams.isRegexEnabled, isRegexEnabled ? "true" : null], [SearchQueryParams.isCaseSensitivityEnabled, isCaseSensitivityEnabled ? "true" : null], ); + + if (showLoginWall) { + if (query.trim().length === 0) { + return; + } + captureEvent('wa_publicsaas_cs_login_wall_prompted', {}); + setLoginCallbackUrl(url); + return; + } + router.push(url); - }, [router, isRegexEnabled, isCaseSensitivityEnabled]); + }, [ + captureEvent, + isCaseSensitivityEnabled, + isRegexEnabled, + router, + showLoginWall, + ]); return (
+ { + if (!open) { + setLoginCallbackUrl(undefined); + } + }} + callbackUrl={loginCallbackUrl} + />
) } diff --git a/packages/web/src/app/(app)/search/components/searchLandingPage.tsx b/packages/web/src/app/(app)/search/components/searchLandingPage.tsx index f83d2aed3..762e49914 100644 --- a/packages/web/src/app/(app)/search/components/searchLandingPage.tsx +++ b/packages/web/src/app/(app)/search/components/searchLandingPage.tsx @@ -10,10 +10,12 @@ import { isServiceError } from "@/lib/utils" export interface SearchLandingPageProps { isSearchAssistSupported: boolean; + showLoginWall: boolean; } export const SearchLandingPage = async ({ isSearchAssistSupported, + showLoginWall, }: SearchLandingPageProps) => { const carouselRepos = await getRepos({ where: { @@ -39,6 +41,7 @@ export const SearchLandingPage = async ({ autoFocus={true} className="border-none pt-0.5 pb-0" isSearchAssistSupported={isSearchAssistSupported} + showLoginWall={showLoginWall} />
diff --git a/packages/web/src/app/(app)/search/components/searchResultsPage.tsx b/packages/web/src/app/(app)/search/components/searchResultsPage.tsx index dcb5f4aea..8ac79ac4e 100644 --- a/packages/web/src/app/(app)/search/components/searchResultsPage.tsx +++ b/packages/web/src/app/(app)/search/components/searchResultsPage.tsx @@ -39,6 +39,7 @@ interface SearchResultsPageProps { isRegexEnabled: boolean; isCaseSensitivityEnabled: boolean; isSearchAssistSupported: boolean; + showLoginWall: boolean; } export const SearchResultsPage = ({ @@ -47,6 +48,7 @@ export const SearchResultsPage = ({ isRegexEnabled, isCaseSensitivityEnabled, isSearchAssistSupported, + showLoginWall, }: SearchResultsPageProps) => { const router = useRouter(); const { setSearchHistory } = useSearchHistory(); @@ -179,6 +181,7 @@ export const SearchResultsPage = ({ }} className="w-full" isSearchAssistSupported={isSearchAssistSupported} + showLoginWall={showLoginWall} />
diff --git a/packages/web/src/app/(app)/search/page.tsx b/packages/web/src/app/(app)/search/page.tsx index 97e552b03..038646942 100644 --- a/packages/web/src/app/(app)/search/page.tsx +++ b/packages/web/src/app/(app)/search/page.tsx @@ -1,4 +1,5 @@ import { env } from "@sourcebot/shared"; +import { auth } from "@/auth"; import { SearchLandingPage } from "./components/searchLandingPage"; import { SearchResultsPage } from "./components/searchResultsPage"; import { getConfiguredLanguageModelsInfo } from "@/features/chat/utils.server"; @@ -16,12 +17,19 @@ export default async function SearchPage(props: SearchPageProps) { const query = searchParams?.query; const isRegexEnabled = searchParams?.isRegexEnabled === "true"; const isCaseSensitivityEnabled = searchParams?.isCaseSensitivityEnabled === "true"; + const session = await auth(); + const showLoginWall = env.EXPERIMENT_ASK_GH_ENABLED === "true" && !session?.user; const languageModels = await getConfiguredLanguageModelsInfo(); const isSearchAssistSupported = languageModels.length > 0; if (query === undefined || query.length === 0) { - return + return ( + + ) } return ( @@ -31,6 +39,7 @@ export default async function SearchPage(props: SearchPageProps) { isRegexEnabled={isRegexEnabled} isCaseSensitivityEnabled={isCaseSensitivityEnabled} isSearchAssistSupported={isSearchAssistSupported} + showLoginWall={showLoginWall} /> ) } diff --git a/packages/web/src/features/chat/components/chatBox/loginDialog.tsx b/packages/web/src/app/components/loginDialog.tsx similarity index 91% rename from packages/web/src/features/chat/components/chatBox/loginDialog.tsx rename to packages/web/src/app/components/loginDialog.tsx index 9fc83d257..75f11a3fd 100644 --- a/packages/web/src/features/chat/components/chatBox/loginDialog.tsx +++ b/packages/web/src/app/components/loginDialog.tsx @@ -13,11 +13,13 @@ import { usePathname } from "next/navigation"; interface LoginDialogProps { isOpen: boolean; onOpenChange: (open: boolean) => void; + callbackUrl?: string; } export const LoginDialog = ({ isOpen, onOpenChange, + callbackUrl, }: LoginDialogProps) => { const pathname = usePathname(); @@ -33,7 +35,7 @@ export const LoginDialog = ({
diff --git a/packages/web/src/features/chat/components/chatBox/chatBox.tsx b/packages/web/src/features/chat/components/chatBox/chatBox.tsx index 8ee066852..57eff7b4a 100644 --- a/packages/web/src/features/chat/components/chatBox/chatBox.tsx +++ b/packages/web/src/features/chat/components/chatBox/chatBox.tsx @@ -24,7 +24,7 @@ import { useSuggestionsData } from "./useSuggestionsData"; import { useToast } from "@/components/hooks/use-toast"; import { SearchContextQuery } from "@/lib/types"; import isEqual from "fast-deep-equal/react"; -import { LoginDialog } from "./loginDialog"; +import { LoginDialog } from "@/app/components/loginDialog"; import { usePathname } from "next/navigation"; import { ATTACHMENT_MAX_IMAGE_BYTES, ATTACHMENT_MAX_TURN_TEXT_BYTES, PENDING_CHAT_SUBMISSION_SESSION_STORAGE_KEY } from "@/features/chat/constants"; import useCaptureEvent from "@/hooks/useCaptureEvent"; diff --git a/packages/web/src/lib/posthogEvents.ts b/packages/web/src/lib/posthogEvents.ts index 5859bf560..ae1685383 100644 --- a/packages/web/src/lib/posthogEvents.ts +++ b/packages/web/src/lib/posthogEvents.ts @@ -537,6 +537,7 @@ export type PosthogEventMap = { }, ////////////////////////////////////////////////////////////////// wa_askgh_login_wall_prompted: {}, + wa_publicsaas_cs_login_wall_prompted: {}, ////////////////////////////////////////////////////////////////// askgh_repo_index_requested: { owner: string,