diff --git a/src/webviews/api/configuration/appRouter.ts b/src/webviews/api/configuration/appRouter.ts index e45e9d976..f7fb3d5be 100644 --- a/src/webviews/api/configuration/appRouter.ts +++ b/src/webviews/api/configuration/appRouter.ts @@ -12,7 +12,7 @@ import { z } from 'zod'; import { type API } from '../../../AzureDBExperiences'; import { collectionsViewRouter as collectionViewRouter } from '../../mongoClusters/collectionView/collectionViewRouter'; import { documentsViewRouter as documentViewRouter } from '../../mongoClusters/documentView/documentsViewRouter'; -import { publicProcedure, router } from '../extension-server/trpc'; +import { publicProcedure, router, trpcToTelemetry } from '../extension-server/trpc'; /** * You can read more about tRPC here: @@ -29,6 +29,7 @@ import { publicProcedure, router } from '../extension-server/trpc'; export type BaseRouterContext = { dbExperience: API; webviewName: string; + signal?: AbortSignal; // This is a special property that is used to cancel subscriptions }; /** @@ -108,6 +109,151 @@ const commonRouter = router({ detail: input.modal ? input.cause : undefined, // The content of the 'detail' field is only shown when modal is true }); }), +}); + +const demoRouter = router({ + /** + * Example Subscription Procedure: `demoBasicSubscription` + * + * This subscription demonstrates how to stream data from the server to the client over time + * using asynchronous generators (the tRPC v11/v12 approach). + * + * **How Subscriptions Work in tRPC:** + * - A subscription procedure is defined using `publicProcedure.subscription(async function*(opts) { ... })`. + * - Inside the async generator, you `yield` values over time. Each yielded value is sent to the client. + * - The subscription remains active until one of the following occurs: + * 1. The server side returns from the async generator function (e.g., after certain logic or conditions). + * 2. An error is thrown inside the async generator, causing the subscription to terminate with an error. + * 3. The client unsubscribes (calling `subscription.unsubscribe()` on the client), which triggers the server to cancel the subscription. + * 4. The server receives an abort signal (such as `ctx.signal.aborted`), and you return early to stop emitting more values. + * + * **Context and Abort Signals:** + * - `ctx` contains an `AbortSignal` (`ctx.signal`) that indicates when the client wants to stop the subscription. + * - By checking `if (ctx.signal?.aborted)`, you can gracefully end the subscription. This ensures no further values are emitted. + * + * **Usage Example (on the Client):** + * ```typescript + * const subscription = trpcClient.demo.demoBasicSubscription.subscribe(undefined, { + * onStarted() { + * console.log('Subscription started'); + * }, + * onData(data) { + * console.log('Received subscription data:', data); + * if (data === 5) { + * // Manually unsubscribe after receiving the number 5 + * subscription.unsubscribe(); + * // Note: onComplete() will not be called because we're forcefully unsubscribing here + * } + * }, + * onError(err) { + * console.error('Subscription error:', err); + * }, + * onComplete() { + * console.log('Subscription completed'); + * } + * }); + * ``` + * + * **Key Points:** + * - Subscriptions can produce multiple values over time. + * - You decide when to stop by returning or by the client unsubscribing. + * - Error handling and completion are well-defined; the client receives these signals via callbacks. + */ + demoBasicSubscription: publicProcedure.subscription(async function* ({ ctx }) { + const myCtx = ctx as BaseRouterContext; + + let count = 0; + while (true) { + // Simulate work or data updates by delaying each emission + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // Optionally, you can stop emitting values after a certain condition: + // if (count > 2) { + // return; // This completes the subscription after three iterations (0, 1, 2) + // } + + // Check if the client has aborted (unsubscribed) before yielding the next value + if (myCtx.signal?.aborted) { + // If aborted, just return to end the subscription gracefully + return; + } + + // Yield the next value of `count`. This value is sent to the client as soon as possible. + yield count++; + } + }), + /** + * Example Subscription Procedure: `demoComplexSubscription` + * + * This subscription demonstrates handling more complex inputs and outputs compared to a simple counter. + * + * **Key Points:** + * - Inputs are validated using Zod, ensuring the caller provides the correct structure: + * - `start` and `end`: define the numeric range of values to emit. + * - `abortAt`: an optional number that, if reached or surpassed, causes the subscription to end. + * - Each emitted value is delayed to simulate work or streaming data updates. + * - The subscription checks both a user-defined cutoff (`abortAt`) and the `ctx.signal` abort + * mechanism to decide when to stop. + * - Yielded data includes the current value, a `done` flag, and the original input, showing how + * you can return rich, structured results at each emission. + * + * **Example Usage on the Client:** + * ```typescript + * const subscription = trpcClient.demo.demoComplexSubscription.subscribe( + * { start: 1, end: 10, abortAt: 5 }, + * { + * onStarted() { + * console.log('Complex subscription started'); + * }, + * onData(data) { + * console.log('Received complex subscription data:', data); + * }, + * onError(err) { + * console.error('Complex subscription error:', err); + * }, + * onComplete() { + * console.log('Complex subscription completed'); + * } + * } + * ); + * ``` + * + * This setup shows how you can manage more intricate logic in a subscription: + * - Emitting a range of values. + * - Allowing the client to define when to stop via `abortAt`. + * - Handling external abort signals for graceful termination. + * - Returning additional contextual data (`originalInput`) along with each value. + */ + demoComplexSubscription: publicProcedure + .use(trpcToTelemetry) + .input( + z.object({ + start: z.number(), + end: z.number(), + abortAt: z.optional(z.number()), + }), + ) + .subscription(async function* ({ input, ctx }) { + const myCtx = ctx as BaseRouterContext; + + for (let i = input.start; i <= input.end; i++) { + // Simulate work or data updates by delaying each emission + await new Promise((resolve) => setTimeout(resolve, 2000)); + + if (input.abortAt && i >= input.abortAt) { + // If the specified abortAt value is reached, stop emitting. + return; + } + + // Check if the client has aborted (unsubscribed) before yielding the next value + if (myCtx.signal?.aborted) { + // If aborted, just return to end the subscription gracefully + return; + } + + yield { value: i, done: false, originalInput: input }; + } + }), hello: publicProcedure // This is the input schema of your procedure, no parameters .query(async () => { @@ -129,6 +275,7 @@ const commonRouter = router({ // eslint-disable-next-line @typescript-eslint/no-unused-vars export const appRouter = router({ common: commonRouter, + demo: demoRouter, mongoClusters: { documentView: documentViewRouter, collectionView: collectionViewRouter, diff --git a/src/webviews/api/extension-server/WebviewController.ts b/src/webviews/api/extension-server/WebviewController.ts index 56f259cf1..8bb14d26c 100644 --- a/src/webviews/api/extension-server/WebviewController.ts +++ b/src/webviews/api/extension-server/WebviewController.ts @@ -7,28 +7,31 @@ import { getTRPCErrorFromUnknown } from '@trpc/server'; import * as vscode from 'vscode'; import { type API } from '../../../AzureDBExperiences'; import { appRouter, type BaseRouterContext } from '../configuration/appRouter'; -import { type VsCodeLinkNotification, type VsCodeLinkRequestMessage } from '../webview-client/vscodeLink'; +import { type VsCodeLinkRequestMessage } from '../webview-client/vscodeLink'; import { WebviewBaseController } from './WebviewBaseController'; import { createCallerFactory } from './trpc'; /** * WebviewController is a class that manages a vscode.WebviewPanel and provides - * a way to communicate with it. It provides a way to register request handlers and reducers - * that can be called from the webview. It also provides a way to post notifications to the webview. - * @template Configuration The type of the configuration object that the webview will receive - * @template Reducers The type of the reducers that the webview will use + * a way to communicate with it. It uses tRPC to handle incoming requests (queries, + * mutations, and subscriptions) from the webview. Through this controller, the + * webview can call server-side procedures defined in the `appRouter`. + * + * @template Configuration - The type of the configuration object that the webview will receive. */ export class WebviewController extends WebviewBaseController { private _panel: vscode.WebviewPanel; /** - * Creates a new WebviewController - * @param context The context of the extension-server - * @param title The title of the webview panel - * @param webviewName The source file that the webview will use - * @param initialState The initial state object that the webview will use - * @param viewColumn The view column that the webview will be displayed in - * @param _iconPath The icon path that the webview will use + * Creates a new WebviewController instance. + * + * @param context The extension context. + * @param dbExperience A reference to the API object associated with this webview. + * @param title The title of the webview panel. + * @param webviewName The identifier/name for the webview resource. + * @param initialState The initial state object that the webview will use on startup. + * @param viewColumn The view column in which to show the new webview panel. + * @param _iconPath An optional icon to display in the tab of the webview. */ constructor( context: vscode.ExtensionContext, @@ -46,6 +49,7 @@ export class WebviewController extends WebviewBaseController extends WebviewBaseController { this.dispose(); }), ); - // This call sends messages to the Webview so it's called after the Webview creation. + // Initializes the base functionality (like sending initial configuration) after creating the panel this.initializeBase(); } - protected setupTrpc(context: BaseRouterContext): void { - const callerFactory = createCallerFactory(appRouter); + /** + * A map tracking active subscriptions by their operation ID. + * Each subscription is associated with an AbortController, allowing the server + * side to cancel the subscription if requested by the client. + */ + protected _activeSubscriptions = new Map(); + /** + * Sets up tRPC integration for the webview. This includes listening for messages from the webview, + * parsing them as tRPC operations (queries, mutations, subscriptions, or subscription stops), + * invoking the appropriate server-side procedures, and returning results or errors. + * + * After refactoring, the switch-case is now delegated to separate handler functions + * for improved clarity. + * + * @param context - The base router context for procedure calls. + */ + protected setupTrpc(context: BaseRouterContext): void { this.registerDisposable( this._panel.webview.onDidReceiveMessage(async (message: VsCodeLinkRequestMessage) => { - // Create a caller with the necessary context (currrently none, but maybe a telemetry hook later?) - const caller = callerFactory(context); - switch (message.op.type) { - // case 'subscription': - // break; - // case 'subscription.stop': - // break; + case 'subscription': + await this.handleSubscriptionMessage(message, context); + break; + + case 'subscription.stop': + this.handleSubscriptionStopMessage(message); + break; + default: - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const procedure = caller[message.op.path]; + await this.handleDefaultMessage(message, context); + break; + } + }), + ); + } - if (typeof procedure !== 'function') { - throw new Error(`Procedure not found: ${message.op.path}`); - } + /** + * Handles the 'subscription' message type. + * + * Sets up an async iterator for the subscription procedure and streams results back + * to the webview. Also handles cancellation via AbortController. + * + * @param message - The original message from the webview. + * @param caller - The tRPC caller for invoking the subscription procedure. + * @param context - The base router context, to which we add an abort signal. + */ + private async handleSubscriptionMessage(message: VsCodeLinkRequestMessage, context: BaseRouterContext) { + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const callerFactory = createCallerFactory(appRouter); + const caller = callerFactory(context); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call - const result = await procedure(message.op.input); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const procedure = caller[message.op.path]; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const response = { id: message.id, result }; + if (typeof procedure !== 'function') { + throw new Error(`Procedure not found: ${message.op.path}`); + } - this._panel.webview.postMessage(response); - } catch (error) { - const trpcErrorMessage = this.wrapInTrpcErrorMessage(error, message.id); - this._panel.webview.postMessage(trpcErrorMessage); - } + // In v12, tRPC will have better cancellation support. For now, we use AbortController. + const abortController = new AbortController(); + this._activeSubscriptions.set(message.id, abortController); - break; + // Attach the abort signal to the context for the subscription + context.signal = abortController.signal; + + // Await the procedure call to get the async iterator (async generator) for the subscription + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + const asyncIter = await procedure(message.op.input); + + void (async () => { + try { + for await (const value of asyncIter) { + // Each yielded value is sent to the webview + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + this._panel.webview.postMessage({ id: message.id, result: value }); + } + + // On natural completion, inform the client + this._panel.webview.postMessage({ id: message.id, complete: true }); + } catch (error) { + const trpcErrorMessage = this.wrapInTrpcErrorMessage(error, message.id); + this._panel.webview.postMessage(trpcErrorMessage); + } finally { + this._activeSubscriptions.delete(message.id); } - }), - ); + })(); + } catch (error) { + const trpcErrorMessage = this.wrapInTrpcErrorMessage(error, message.id); + this._panel.webview.postMessage(trpcErrorMessage); + } } /** - * Wraps an error into a TRPC error message format suitable for sending via `postMessage`. + * Handles the 'subscription.stop' message type. * - * This function manually constructs the error object by extracting the necessary properties - * from the `errorEntry`. This is important because when using `postMessage` to send data - * from the extension to the webview, the data is serialized (e.g., using `JSON.stringify`). - * During serialization, only own enumerable properties of the object are included, while - * properties inherited from the prototype chain or non-enumerable properties are omitted. + * Looks up the active subscription by ID and aborts it, stopping further data emission. * - * Error objects like instances of `Error` or `TRPCError` often have their properties - * (such as `message`, `name`, and `stack`) either inherited from the prototype or defined - * as non-enumerable. As a result, directly passing such error objects to `postMessage` - * would result in the webview receiving an error object without these essential properties. + * @param message - The original message from the webview. + */ + private handleSubscriptionStopMessage(message: VsCodeLinkRequestMessage) { + const abortController = this._activeSubscriptions.get(message.id); + if (abortController) { + abortController.abort(); + this._activeSubscriptions.delete(message.id); + } + } + + /** + * Handles the default case for messages (i.e., queries and mutations). * - * By explicitly constructing a plain object with the required error properties, we ensure - * that all necessary information is included during serialization and properly received - * by the webview. + * Calls the specified tRPC procedure and returns a single result. + * If the procedure is not found or throws, returns an error message. * - * @param error - The error to be wrapped. - * @param operationId - The ID of the operation associated with the error. - * @returns An object containing the operation ID and a plain error object with own enumerable properties. + * @param message - The original message from the webview. + * @param caller - The tRPC caller for invoking the procedure. + */ + private async handleDefaultMessage(message: VsCodeLinkRequestMessage, context: BaseRouterContext) { + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const callerFactory = createCallerFactory(appRouter); + const caller = callerFactory(context); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const procedure = caller[message.op.path]; + + if (typeof procedure !== 'function') { + throw new Error(`Procedure not found: ${message.op.path}`); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + const result = await procedure(message.op.input); + + // Send the result back to the client + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const response = { id: message.id, result }; + this._panel.webview.postMessage(response); + } catch (error) { + const trpcErrorMessage = this.wrapInTrpcErrorMessage(error, message.id); + this._panel.webview.postMessage(trpcErrorMessage); + } + } + + /** + * Converts an unknown error into a tRPC-compatible error response. + * + * By constructing a plain object with enumerable properties, we ensure the client + * receives a properly serialized error object over postMessage. + * + * @param error - The caught error. + * @param operationId - The operation ID associated with the error. */ wrapInTrpcErrorMessage(error: unknown, operationId: string) { const errorEntry = getTRPCErrorFromUnknown(error); @@ -143,32 +238,28 @@ export class WebviewController extends WebviewBaseController { - * const { trpcClient, vscodeEventTarget } = useTrpcClient(); - * - * // Listen for notifications from the extension - * useEffect(() => { - * const handleNotification = (event: Event) => { - * const customEvent = event as CustomEvent; - * const notification = customEvent.detail; - * // Handle the notification data - * console.log('Received notification:', notification); - * }; - * - * vscodeEventTarget.addEventListener('VsCodeLinkNotification', handleNotification); - * - * return () => { - * vscodeEventTarget.removeEventListener('VsCodeLinkNotification', handleNotification); - * }; - * }, [vscodeEventTarget]); + * const { trpcClient } = useTrpcClient(); * * // Use the tRPC client to make queries and mutations * useEffect(() => { @@ -61,9 +38,6 @@ import { export function useTrpcClient() { const { vscodeApi } = useContext(WebviewContext); - // Create an EventTarget instance per webview to dispatch and listen to custom events - const vscodeEventTarget = useMemo(() => new EventTarget(), []); - /** * Function to send messages to the VSCode extension. * @@ -113,36 +87,6 @@ export function useTrpcClient() { [vscodeApi], ); - /** - * Note to code maintainers: - * This `useEffect` sets up a persistent listener for messages from the VSCode extension. - * It specifically listens for messages of type 'VSLinkNotification' and dispatches them - * as custom events on the `vscodeEventTarget`. This allows components to subscribe to - * notifications from the extension. - * - * Be careful when modifying this handler, as it needs to correctly identify and process - * notification messages without interfering with tRPC messages. - */ - // Set up a persistent notification handler to listen for messages from the extension - useEffect(() => { - const handler = (event: MessageEvent) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (event.data && typeof event.data === 'object' && event.data.type === 'VSLinkNotification') { - // Dispatch the notification to the EventTarget - const customEvent = new CustomEvent('VsCodeLinkNotification', { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - detail: event.data.payload as VsCodeLinkNotification, - }); - vscodeEventTarget.dispatchEvent(customEvent); - } - }; - - window.addEventListener('message', handler); - return () => { - window.removeEventListener('message', handler); - }; - }, [vscodeEventTarget]); - - // Return the tRPC client and the event target for notifications - return { trpcClient: trpcClient, vscodeEventTarget }; + // Return the tRPC client + return { trpcClient: trpcClient }; } diff --git a/src/webviews/api/webview-client/vscodeLink.ts b/src/webviews/api/webview-client/vscodeLink.ts index c8303b718..8c156cbbd 100644 --- a/src/webviews/api/webview-client/vscodeLink.ts +++ b/src/webviews/api/webview-client/vscodeLink.ts @@ -8,13 +8,18 @@ import { TRPCClientError, type Operation, type TRPCLink } from '@trpc/client'; import { observable } from '@trpc/server/observable'; // Their example uses a reference from /server/ and so do we: https://trpc.io/docs/client/links#example import { type AppRouter } from '../configuration/appRouter'; +type StopOperation = Omit, 'type'> & { + type: 'subscription.stop'; +}; + /** * Messages sent from the webview/client to the extension/server. * @id - A unique identifier for the message/ */ export interface VsCodeLinkRequestMessage { id: string; - op: Operation; + // TODO, when tRPC v12 is released, 'subscription.stop' should be supported natively, until then, we're adding it manually. + op: Operation | StopOperation; } /** @@ -38,11 +43,6 @@ export interface VsCodeLinkResponseMessage { complete?: boolean; } -export interface VsCodeLinkNotification { - notification: string; - parameters: unknown; -} - interface VSCodeLinkOptions { // Function to send a message to the server / extension send: (message: VsCodeLinkRequestMessage) => void; @@ -99,7 +99,6 @@ function vscodeLink(options: VSCodeLinkOptions): TRPCLink { // Ignore messages not related to this operation if (message.id !== operationId) return; - // Handle error messages --> this codepath hasn't been explored yet if (message.error) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument observer.error(TRPCClientError.from(message.error)); @@ -122,7 +121,7 @@ function vscodeLink(options: VSCodeLinkOptions): TRPCLink { } } - // TODO: Handle completion signals for subscriptions + // Handle completion signals for subscriptions if (message.complete) { observer.complete(); } @@ -135,12 +134,11 @@ function vscodeLink(options: VSCodeLinkOptions): TRPCLink { send({ id: operationId, op }); // Return a cleanup function that is called when the observable is unsubscribed + // This is relevant when working with subscriptions. return () => { // If it's a subscription, send a stop message to the server if (op.type === 'subscription') { - // TODO: subscriptions support - //console.log('🤯🤯🤯 here stop the subscription: ' + op.path); - //send({ id: operationId, op: { ...op, type: 'subscription.stop' } }); + send({ id: operationId, op: { ...op, type: 'subscription.stop' } }); } // Cleanup the message handler unsubscribe(); diff --git a/src/webviews/mongoClusters/collectionView/CollectionView.tsx b/src/webviews/mongoClusters/collectionView/CollectionView.tsx index 9ba2ce8e7..bb45f191f 100644 --- a/src/webviews/mongoClusters/collectionView/CollectionView.tsx +++ b/src/webviews/mongoClusters/collectionView/CollectionView.tsx @@ -43,10 +43,9 @@ export const CollectionView = (): JSX.Element => { //const configuration = useConfiguration(); /** - * Use the `useTrpcClient` hook to get the tRPC client and an event target - * for handling notifications from the extension. + * Use the `useTrpcClient` hook to get the tRPC client */ - const { trpcClient /** , vscodeEventTarget */ } = useTrpcClient(); + const { trpcClient } = useTrpcClient(); /** * Please note: using the context and states inside of closures can lead to stale data. diff --git a/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarMainView.tsx b/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarMainView.tsx index bcc351bf5..6f6152961 100644 --- a/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarMainView.tsx +++ b/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarMainView.tsx @@ -21,10 +21,9 @@ export const ToolbarMainView = (): JSX.Element => { const ToolbarQueryOperations = (): JSX.Element => { /** - * Use the `useTrpcClient` hook to get the tRPC client and an event target - * for handling notifications from the extension. + * Use the `useTrpcClient` hook to get the tRPC client */ - const { trpcClient /** , vscodeEventTarget */ } = useTrpcClient(); + const { trpcClient } = useTrpcClient(); const [currentContext, setCurrentContext] = useContext(CollectionViewContext); @@ -113,7 +112,7 @@ const ToolbarQueryOperations = (): JSX.Element => { const ToolbarDataOperations = (): JSX.Element => { const [currentContext] = useContext(CollectionViewContext); - const { trpcClient /** , vscodeEventTarget */ } = useTrpcClient(); + const { trpcClient } = useTrpcClient(); const handleImportFromJson = () => { void trpcClient.mongoClusters.collectionView.importDocuments.query(); diff --git a/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarTableNavigation.tsx b/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarTableNavigation.tsx index 3e5f16492..7fad70a01 100644 --- a/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarTableNavigation.tsx +++ b/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarTableNavigation.tsx @@ -21,10 +21,9 @@ import { CollectionViewContext, Views } from '../../collectionViewContext'; export const ToolbarTableNavigation = (): JSX.Element => { /** - * Use the `useTrpcClient` hook to get the tRPC client and an event target - * for handling notifications from the extension. + * Use the `useTrpcClient` hook to get the tRPC client */ - const { trpcClient /** , vscodeEventTarget */ } = useTrpcClient(); + const { trpcClient } = useTrpcClient(); const [currentContext, setCurrentContext] = useContext(CollectionViewContext); diff --git a/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarViewNavigation.tsx b/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarViewNavigation.tsx index 92b079e58..fbfc3d1da 100644 --- a/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarViewNavigation.tsx +++ b/src/webviews/mongoClusters/collectionView/components/toolbar/ToolbarViewNavigation.tsx @@ -12,10 +12,9 @@ import { ToolbarDividerTransparent } from './ToolbarDividerTransparent'; export const ToolbarViewNavigation = (): JSX.Element => { /** - * Use the `useTrpcClient` hook to get the tRPC client and an event target - * for handling notifications from the extension. + * Use the `useTrpcClient` hook to get the tRPC client */ - const { trpcClient /** , vscodeEventTarget */ } = useTrpcClient(); + const { trpcClient } = useTrpcClient(); const [currentContext, setCurrentContext] = useContext(CollectionViewContext); diff --git a/src/webviews/mongoClusters/documentView/documentView.tsx b/src/webviews/mongoClusters/documentView/documentView.tsx index 752f86bb1..fd1740ab6 100644 --- a/src/webviews/mongoClusters/documentView/documentView.tsx +++ b/src/webviews/mongoClusters/documentView/documentView.tsx @@ -38,10 +38,9 @@ export const DocumentView = (): JSX.Element => { const configuration = useConfiguration(); /** - * Use the `useTrpcClient` hook to get the tRPC client and an event target - * for handling notifications from the extension. + * Use the `useTrpcClient` hook to get the tRPC client */ - const { trpcClient /*, vscodeEventTarget*/ } = useTrpcClient(); + const { trpcClient } = useTrpcClient(); const [editorContent] = configuration.mode === 'add' ? useState('{ }') : useState('{ "loading...": true }'); const [isLoading, setIsLoading] = useState(configuration.mode !== 'add'); @@ -76,22 +75,6 @@ export const DocumentView = (): JSX.Element => { const getCurrentContent = () => editorRef.current?.getValue() || ''; const setContent = (newValue: string) => editorRef.current?.setValue(newValue); - // useEffect(() => { // example of handling notifications from the extension - // const handleNotification = (event: Event) => { - // const customEvent = event as CustomEvent; - // const notification = customEvent.detail; - - // // Handle the notification data, just playing with it - // console.log('Handling notification:', notification); - // }; - - // vscodeEventTarget.addEventListener('VsCodeLinkNotification', handleNotification); - - // return () => { - // vscodeEventTarget.removeEventListener('VsCodeLinkNotification', handleNotification); - // }; - // }, [vscodeEventTarget]); - const handleMonacoEditorMount = ( editor: monacoEditor.editor.IStandaloneCodeEditor, _monaco: typeof monacoEditor,