Switch to new welcome view (#9741)

This commit is contained in:
Matt Rubens 2025-12-02 10:02:12 -05:00 committed by GitHub
parent 906c6f0de4
commit 152af1474e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 2 additions and 294 deletions

View file

@ -1,7 +1,6 @@
import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"
import { useEvent } from "react-use"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import posthog from "posthog-js"
import { ExtensionMessage } from "@roo/ExtensionMessage"
import TranslationProvider from "./i18n/TranslationContext"
@ -15,8 +14,7 @@ import { ExtensionStateContextProvider, useExtensionState } from "./context/Exte
import ChatView, { ChatViewRef } from "./components/chat/ChatView"
import HistoryView from "./components/history/HistoryView"
import SettingsView, { SettingsViewRef } from "./components/settings/SettingsView"
import WelcomeView from "./components/welcome/WelcomeView"
import WelcomeViewProvider from "./components/welcome/WelcomeViewProvider"
import WelcomeView from "./components/welcome/WelcomeViewProvider"
import { MarketplaceView } from "./components/marketplace/MarketplaceView"
import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog"
import { CheckpointRestoreDialog } from "./components/chat/CheckpointRestoreDialog"
@ -79,21 +77,6 @@ const App = () => {
mdmCompliant,
} = useExtensionState()
const [useProviderSignupView, setUseProviderSignupView] = useState(false)
// Check PostHog feature flag for provider signup view
// Wait for telemetry to be initialized before checking feature flags
useEffect(() => {
if (!didHydrateState || telemetrySetting === "disabled") {
return
}
posthog.onFeatureFlags(function () {
// Feature flag for new provider-focused welcome view
setUseProviderSignupView(posthog?.getFeatureFlag("welcome-provider-signup") === "test")
})
}, [didHydrateState, telemetrySetting])
// Create a persistent state manager
const marketplaceStateManager = useMemo(() => new MarketplaceViewStateManager(), [])
@ -260,11 +243,7 @@ const App = () => {
// Do not conditionally load ChatView, it's expensive and there's state we
// don't want to lose (user input, disableInput, askResponse promise, etc.)
return showWelcome ? (
useProviderSignupView ? (
<WelcomeViewProvider />
) : (
<WelcomeView />
)
<WelcomeView />
) : (
<>
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}

View file

@ -2,18 +2,9 @@
import React from "react"
import { render, screen, act, cleanup } from "@/utils/test-utils"
import posthog from "posthog-js"
import AppWithProviders from "../App"
// Mock posthog
vi.mock("posthog-js", () => ({
default: {
onFeatureFlags: vi.fn(),
getFeatureFlag: vi.fn(),
},
}))
vi.mock("@src/utils/vscode", () => ({
vscode: {
postMessage: vi.fn(),
@ -312,54 +303,4 @@ describe("App", () => {
expect(chatView.getAttribute("data-hidden")).toBe("false")
expect(screen.queryByTestId("marketplace-view")).not.toBeInTheDocument()
})
describe("PostHog feature flag initialization", () => {
it("waits for state hydration before checking feature flags", () => {
mockUseExtensionState.mockReturnValue({
didHydrateState: false,
showWelcome: false,
shouldShowAnnouncement: false,
experiments: {},
language: "en",
telemetrySetting: "enabled",
})
render(<AppWithProviders />)
// PostHog feature flag check should not be called before hydration
expect(posthog.onFeatureFlags).not.toHaveBeenCalled()
})
it("checks feature flags after state hydration when telemetry is enabled", () => {
mockUseExtensionState.mockReturnValue({
didHydrateState: true,
showWelcome: false,
shouldShowAnnouncement: false,
experiments: {},
language: "en",
telemetrySetting: "enabled",
})
render(<AppWithProviders />)
// PostHog feature flag check should be called after hydration
expect(posthog.onFeatureFlags).toHaveBeenCalled()
})
it("does not check feature flags when telemetry is disabled", () => {
mockUseExtensionState.mockReturnValue({
didHydrateState: true,
showWelcome: false,
shouldShowAnnouncement: false,
experiments: {},
language: "en",
telemetrySetting: "disabled",
})
render(<AppWithProviders />)
// PostHog feature flag check should not be called when telemetry is disabled
expect(posthog.onFeatureFlags).not.toHaveBeenCalled()
})
})
})

View file

@ -1,212 +0,0 @@
import { useCallback, useState, useEffect } from "react"
import knuthShuffle from "knuth-shuffle-seeded"
import { Trans } from "react-i18next"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import posthog from "posthog-js"
import type { ProviderSettings } from "@roo-code/types"
import { TelemetryEventName } from "@roo-code/types"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import { validateApiConfiguration } from "@src/utils/validate"
import { vscode } from "@src/utils/vscode"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { getRequestyAuthUrl, getOpenRouterAuthUrl } from "@src/oauth/urls"
import { telemetryClient } from "@src/utils/TelemetryClient"
import { Button } from "@src/components/ui"
import ApiOptions from "../settings/ApiOptions"
import { Tab, TabContent } from "../common/Tab"
import RooHero from "./RooHero"
const WelcomeView = () => {
const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme, machineId } = useExtensionState()
const { t } = useAppTranslation()
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
const [showRooProvider, setShowRooProvider] = useState(false)
// Check PostHog feature flag for Roo provider
useEffect(() => {
posthog.onFeatureFlags(function () {
setShowRooProvider(posthog?.getFeatureFlag("roo-provider-featured") === "test")
})
}, [])
// Memoize the setApiConfigurationField function to pass to ApiOptions
const setApiConfigurationFieldForApiOptions = useCallback(
<K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => {
setApiConfiguration({ [field]: value })
},
[setApiConfiguration], // setApiConfiguration from context is stable
)
const handleSubmit = useCallback(() => {
const error = apiConfiguration ? validateApiConfiguration(apiConfiguration) : undefined
if (error) {
setErrorMessage(error)
return
}
setErrorMessage(undefined)
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
}, [apiConfiguration, currentApiConfigName])
// Using a lazy initializer so it reads once at mount
const [imagesBaseUri] = useState(() => {
const w = window as any
return w.IMAGES_BASE_URI || ""
})
return (
<Tab>
<TabContent className="flex flex-col gap-4 p-6">
<RooHero />
<h2 className="mt-0 mb-4 text-xl">{t("welcome:greeting")}</h2>
<div className="text-base text-vscode-foreground py-2 mb-4">
<p className="mb-3 leading-relaxed">
<Trans i18nKey="welcome:introduction" />
</p>
<p className="mb-0 leading-relaxed">
<Trans i18nKey="welcome:chooseProvider" />
</p>
</div>
<div className="mb-4">
<p className="text-sm font-medium mt-4 mb-3">{t("welcome:startRouter")}</p>
<div>
{/* Define the providers */}
{(() => {
// Provider card configuration
const baseProviders = [
{
slug: "requesty",
name: "Requesty",
description: t("welcome:routers.requesty.description"),
incentive: t("welcome:routers.requesty.incentive"),
authUrl: getRequestyAuthUrl(uriScheme),
},
{
slug: "openrouter",
name: "OpenRouter",
description: t("welcome:routers.openrouter.description"),
authUrl: getOpenRouterAuthUrl(uriScheme),
},
]
// Conditionally add Roo provider based on feature flag
const providers = showRooProvider
? [
...baseProviders,
{
slug: "roo",
name: "Roo Code Cloud",
description: t("welcome:routers.roo.description"),
incentive: t("welcome:routers.roo.incentive"),
authUrl: "#", // Placeholder since onClick handler will prevent default
},
]
: baseProviders
// Shuffle providers based on machine ID (will be consistent for the same machine)
const orderedProviders = [...providers]
knuthShuffle(orderedProviders, (machineId as any) || Date.now())
// Render the provider cards
return orderedProviders.map((provider, index) => (
<a
key={index}
href={provider.authUrl}
className="relative flex-1 border border-vscode-panel-border hover:bg-secondary rounded-md py-3 px-4 mb-2 flex flex-row gap-3 cursor-pointer transition-all no-underline text-inherit"
target="_blank"
rel="noopener noreferrer"
onClick={(e) => {
// Track telemetry for featured provider click
telemetryClient.capture(TelemetryEventName.FEATURED_PROVIDER_CLICKED, {
provider: provider.slug,
})
// Special handling for Roo provider
if (provider.slug === "roo") {
e.preventDefault()
// Set the Roo provider configuration
const rooConfig: ProviderSettings = {
apiProvider: "roo",
}
// Save the Roo provider configuration
vscode.postMessage({
type: "upsertApiConfiguration",
text: currentApiConfigName,
apiConfiguration: rooConfig,
})
// Then trigger cloud sign-in
vscode.postMessage({ type: "rooCloudSignIn" })
}
// For other providers, let the default link behavior work
}}>
{provider.incentive && (
<div className="absolute top-0 right-0 text-[10px] text-vscode-badge-foreground bg-vscode-badge-background px-2 py-0.5 rounded-bl rounded-tr-md">
{provider.incentive}
</div>
)}
<div className="w-8 h-8 flex-shrink-0">
<img
src={`${imagesBaseUri}/${provider.slug}.png`}
alt={provider.name}
className="w-full h-full object-contain"
/>
</div>
<div>
<div className="text-sm font-medium text-vscode-foreground">
{provider.name}
</div>
<div className="text-xs text-vscode-descriptionForeground">
{provider.description}
</div>
</div>
</a>
))
})()}
</div>
<p className="text-sm font-medium mt-6 mb-3">{t("welcome:startCustom")}</p>
<ApiOptions
fromWelcomeView
apiConfiguration={apiConfiguration || {}}
uriScheme={uriScheme}
setApiConfigurationField={setApiConfigurationFieldForApiOptions}
errorMessage={errorMessage}
setErrorMessage={setErrorMessage}
/>
</div>
</TabContent>
<div className="sticky bottom-0 bg-vscode-sideBar-background p-4 border-t border-vscode-panel-border">
<div className="flex flex-col gap-2">
<div className="flex justify-end">
<VSCodeLink
href="#"
onClick={(e) => {
e.preventDefault()
vscode.postMessage({ type: "importSettings" })
}}
className="text-sm">
{t("welcome:importSettings")}
</VSCodeLink>
</div>
<Button onClick={handleSubmit} variant="primary">
{t("welcome:start")}
</Button>
{errorMessage && <div className="text-vscode-errorForeground">{errorMessage}</div>}
</div>
</div>
</Tab>
)
}
export default WelcomeView