From 16fc0e08001bed8414f376a4152fe612ae8fce80 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 9 Sep 2025 19:31:10 +0000 Subject: [PATCH] feat: add showCloudPromotion setting to disable cloud CTA - Added showCloudPromotion boolean setting to global-settings.ts (defaults to true) - Updated ExtensionStateContext to handle the new setting - Modified ChatView to respect the setting when displaying RooCloudCTA - Added tests to verify the new functionality - Addresses issue #7825 - allows users to disable cloud promotion --- packages/types/src/global-settings.ts | 3 ++ src/shared/ExtensionMessage.ts | 1 + webview-ui/src/components/chat/ChatView.tsx | 7 +++- .../chat/__tests__/ChatView.spec.tsx | 38 +++++++++++++++++++ .../src/context/ExtensionStateContext.tsx | 13 +++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index f1c4b81c48..3afc1f5f63 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -41,6 +41,7 @@ export const globalSettingsSchema = z.object({ lastShownAnnouncementId: z.string().optional(), customInstructions: z.string().optional(), taskHistory: z.array(historyItemSchema).optional(), + showCloudPromotion: z.boolean().optional(), // Image generation settings (experimental) - flattened for simplicity openRouterImageApiKey: z.string().optional(), @@ -321,6 +322,8 @@ export const EVALS_SETTINGS: RooCodeSettings = { mode: "code", // "architect", customModes: [], + + showCloudPromotion: true, // Default to true to maintain current behavior } export const EVALS_TIMEOUT = 5 * 60 * 1_000 diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 1565bb8c52..648d4fb819 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -209,6 +209,7 @@ export type ExtensionState = Pick< // | "lastShownAnnouncementId" | "customInstructions" // | "taskHistory" // Optional in GlobalSettings, required here. + | "showCloudPromotion" | "autoApprovalEnabled" | "alwaysAllowReadOnly" | "alwaysAllowReadOnlyOutsideWorkspace" diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 3e13905bc9..e9ba034354 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -120,6 +120,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction}
- {cloudIsAuthenticated || taskHistory.length < 4 ? : } + {cloudIsAuthenticated || taskHistory.length < 4 || !showCloudPromotion ? ( + + ) : ( + + )}
{/* Show the task history preview if expanded and tasks exist */} {taskHistory.length > 0 && isExpanded && } diff --git a/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx index 09d46083d4..2aae924bf3 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx @@ -1428,6 +1428,44 @@ describe("ChatView - RooCloudCTA Display Tests", () => { expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument() expect(getByTestId("roo-tips")).toBeInTheDocument() }) + + it("does not show RooCloudCTA when showCloudPromotion is false", () => { + const { queryByTestId, getByTestId } = renderChatView() + + // Set showCloudPromotion to false + act(() => { + mockPostMessage({ + showCloudPromotion: false, + cloudIsAuthenticated: false, + taskHistory: Array(5).fill({ id: "task", ts: Date.now() }), + clineMessages: [], // No active task + }) + }) + + // Should not show RooCloudCTA when showCloudPromotion is false + expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument() + // Should show RooTips instead + expect(getByTestId("roo-tips")).toBeInTheDocument() + }) + + it("shows RooCloudCTA when showCloudPromotion is true and conditions are met", async () => { + const { getByTestId } = renderChatView() + + // Set showCloudPromotion to true with conditions met + act(() => { + mockPostMessage({ + showCloudPromotion: true, + cloudIsAuthenticated: false, + taskHistory: Array(5).fill({ id: "task", ts: Date.now() }), + clineMessages: [], // No active task + }) + }) + + // Should show RooCloudCTA when showCloudPromotion is true and conditions are met + await waitFor(() => { + expect(getByTestId("roo-cloud-cta")).toBeInTheDocument() + }) + }) }) describe("ChatView - Message Queueing Tests", () => { diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 2f4af84f58..5f4dfca51e 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -44,6 +44,8 @@ export interface ExtensionStateContextType extends ExtensionState { mdmCompliant?: boolean hasOpenedModeSelector: boolean // New property to track if user has opened mode selector setHasOpenedModeSelector: (value: boolean) => void // Setter for the new property + showCloudPromotion: boolean // New property for cloud promotion visibility + setShowCloudPromotion: (value: boolean) => void // Setter for cloud promotion alwaysAllowFollowupQuestions: boolean // New property for follow-up questions auto-approve setAlwaysAllowFollowupQuestions: (value: boolean) => void // Setter for the new property followupAutoApproveTimeoutMs: number | undefined // Timeout in ms for auto-approving follow-up questions @@ -255,6 +257,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode maxDiagnosticMessages: 50, openRouterImageApiKey: "", openRouterImageGenerationSelectedModel: "", + showCloudPromotion: true, // Default to true to maintain current behavior }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -274,6 +277,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode global: {}, }) const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true) + const [showCloudPromotion, setShowCloudPromotion] = useState(true) // Default to true const setListApiConfigMeta = useCallback( (value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })), @@ -311,6 +315,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode if ((newState as any).includeTaskHistoryInEnhance !== undefined) { setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance) } + // Update showCloudPromotion if present in state message + if ((newState as any).showCloudPromotion !== undefined) { + setShowCloudPromotion((newState as any).showCloudPromotion) + } // Handle marketplace data if present in state message if (newState.marketplaceItems !== undefined) { setMarketplaceItems(newState.marketplaceItems) @@ -527,6 +535,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }, includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance, + showCloudPromotion, + setShowCloudPromotion: (value) => { + setShowCloudPromotion(value) + setState((prevState) => ({ ...prevState, showCloudPromotion: value })) + }, } return {children}