From 586eb3cd9ed7a56a557bcd98b36dcd80189c681f Mon Sep 17 00:00:00 2001 From: Evan <58194240+celestial-vault@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:40:52 -0700 Subject: [PATCH] migrate accountButtonClicked (#4008) Co-authored-by: Elephant Lumps --- proto/ui.proto | 3 + .../ui/subscribeToAccountButtonClicked.ts | 55 +++++++++++++++++++ src/extension.ts | 27 +++++---- src/shared/ExtensionMessage.ts | 2 +- .../src/context/ExtensionStateContext.tsx | 23 +++++++- 5 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 src/core/controller/ui/subscribeToAccountButtonClicked.ts diff --git a/proto/ui.proto b/proto/ui.proto index 98d04f637a..508c5a0af9 100644 --- a/proto/ui.proto +++ b/proto/ui.proto @@ -37,4 +37,7 @@ service UiService { // Subscribe to chat button clicked events (when the chat button is clicked in VSCode) rpc subscribeToChatButtonClicked(EmptyRequest) returns (stream Empty); + + // Subscribe to account button click events + rpc subscribeToAccountButtonClicked(EmptyRequest) returns (stream Empty); } diff --git a/src/core/controller/ui/subscribeToAccountButtonClicked.ts b/src/core/controller/ui/subscribeToAccountButtonClicked.ts new file mode 100644 index 0000000000..3f66c8410b --- /dev/null +++ b/src/core/controller/ui/subscribeToAccountButtonClicked.ts @@ -0,0 +1,55 @@ +import { Controller } from "../index" +import { Empty, EmptyRequest } from "@shared/proto/common" +import { StreamingResponseHandler, getRequestRegistry } from "../grpc-handler" + +// Track subscriptions by controller ID +const activeSubscriptions = new Map() + +/** + * Subscribe to account button clicked events + * @param controller The controller instance + * @param request The empty request + * @param responseStream The streaming response handler + * @param requestId The request ID for cleanup + */ +export async function subscribeToAccountButtonClicked( + controller: Controller, + request: EmptyRequest, + responseStream: StreamingResponseHandler, + requestId?: string, +): Promise { + const controllerId = controller.id + + // Store subscription with controller ID + activeSubscriptions.set(controllerId, responseStream) + + // Register cleanup + const cleanup = () => { + activeSubscriptions.delete(controllerId) + } + + if (requestId) { + getRequestRegistry().registerRequest(requestId, cleanup, { type: "account_button_subscription" }, responseStream) + } +} + +/** + * Send account button clicked event to a specific controller + * @param controllerId The ID of the controller to send the event to + */ +export async function sendAccountButtonClickedEvent(controllerId: string): Promise { + const responseStream = activeSubscriptions.get(controllerId) + + if (!responseStream) { + console.log(`No active subscription for controller ${controllerId}`) + return + } + + try { + const event: Empty = {} + await responseStream(event, false) + } catch (error) { + console.error(`Error sending account button clicked event to controller ${controllerId}:`, error) + activeSubscriptions.delete(controllerId) + } +} diff --git a/src/extension.ts b/src/extension.ts index c282505073..135726addc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -20,6 +20,7 @@ import { v4 as uuidv4 } from "uuid" import { WebviewProviderType as WebviewProviderTypeEnum } from "@shared/proto/ui" import { WebviewProviderType } from "./shared/webview/types" import { sendHistoryButtonClickedEvent } from "./core/controller/ui/subscribeToHistoryButtonClicked" +import { sendAccountButtonClickedEvent } from "./core/controller/ui/subscribeToAccountButtonClicked" /* Built using https://github.com/microsoft/vscode-webview-ui-toolkit @@ -199,20 +200,22 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.commands.registerCommand("cline.accountButtonClicked", (webview: any) => { - WebviewProvider.getAllInstances().forEach((instance) => { - const openAccount = async (instance?: WebviewProvider) => { - instance?.controller.postMessageToWebview({ - type: "action", - action: "accountButtonClicked", - }) + console.log("[DEBUG] accountButtonClicked", webview) + + const isSidebar = !webview + if (isSidebar) { + const sidebarInstance = WebviewProvider.getSidebarInstance() + if (sidebarInstance) { + // Send event to sidebar controller + sendAccountButtonClickedEvent(sidebarInstance.controller.id) } - const isSidebar = !webview - if (isSidebar) { - openAccount(WebviewProvider.getSidebarInstance()) - } else { - WebviewProvider.getTabInstances().forEach(openAccount) + } else { + // Send to all tab instances + const tabInstances = WebviewProvider.getTabInstances() + for (const instance of tabInstances) { + sendAccountButtonClickedEvent(instance.controller.id) } - }) + } }), ) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index ea2663de21..ececf31550 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -38,7 +38,7 @@ export interface ExtensionMessage { | "fileSearchResults" | "grpc_response" // New type for gRPC responses text?: string - action?: "settingsButtonClicked" | "didBecomeVisible" | "accountLogoutClicked" | "accountButtonClicked" | "focusChatInput" + action?: "settingsButtonClicked" | "didBecomeVisible" | "accountLogoutClicked" | "focusChatInput" state?: ExtensionState images?: string[] files?: string[] diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index c86435c5f3..b24be56c0a 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -196,9 +196,6 @@ export const ExtensionStateContextProvider: React.FC<{ case "settingsButtonClicked": navigateToSettings() break - case "accountButtonClicked": - navigateToAccount() - break } break } @@ -268,6 +265,7 @@ export const ExtensionStateContextProvider: React.FC<{ const mcpButtonUnsubscribeRef = useRef<(() => void) | null>(null) const historyButtonClickedSubscriptionRef = useRef<(() => void) | null>(null) const chatButtonUnsubscribeRef = useRef<(() => void) | null>(null) + const accountButtonClickedSubscriptionRef = useRef<(() => void) | null>(null) // Subscribe to state updates and UI events using the gRPC streaming API useEffect(() => { @@ -400,6 +398,21 @@ export const ExtensionStateContextProvider: React.FC<{ // Still send the webviewDidLaunch message for other initialization vscode.postMessage({ type: "webviewDidLaunch" }) + // Set up account button clicked subscription + accountButtonClickedSubscriptionRef.current = UiServiceClient.subscribeToAccountButtonClicked(EmptyRequest.create(), { + onResponse: () => { + // When account button is clicked, navigate to account view + console.log("[DEBUG] Received account button clicked event from gRPC stream") + navigateToAccount() + }, + onError: (error) => { + console.error("Error in account button clicked subscription:", error) + }, + onComplete: () => { + console.log("Account button clicked subscription completed") + }, + }) + // Clean up subscriptions when component unmounts return () => { if (stateSubscriptionRef.current) { @@ -418,6 +431,10 @@ export const ExtensionStateContextProvider: React.FC<{ chatButtonUnsubscribeRef.current() chatButtonUnsubscribeRef.current = null } + if (accountButtonClickedSubscriptionRef.current) { + accountButtonClickedSubscriptionRef.current() + accountButtonClickedSubscriptionRef.current = null + } } }, [])