From f609de860faf3bdb24266b500b3d64c9989d4936 Mon Sep 17 00:00:00 2001 From: Jake Howell Date: Thu, 5 Mar 2026 15:50:28 +1100 Subject: [PATCH] feat: defer api key generation with mutation (#22318) Closes #22065 This pull-request ensures that when we load the `` we're not instantly attempting to generate an `apiKey` every-time. These are now only generated once the user attempts to actually click on the VSCode link, this is now a mutation also (which is the correct action for this). --- .../pages/WorkspacesPage/WorkspacesTable.tsx | 153 +++++++++++++----- 1 file changed, 113 insertions(+), 40 deletions(-) diff --git a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx index 03ffc4781c..34766f1166 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx @@ -1,6 +1,6 @@ import Skeleton from "@mui/material/Skeleton"; +import { API } from "api/api"; import { templateVersion } from "api/queries/templates"; -import { apiKey } from "api/queries/users"; import { cancelBuild, deleteWorkspace, @@ -629,9 +629,6 @@ type WorkspaceAppsProps = { }; const WorkspaceApps: FC = ({ workspace }) => { - const { data: apiKeyResponse } = useQuery(apiKey()); - const token = apiKeyResponse?.key; - /** * Coder is pretty flexible and allows an enormous variety of use cases, such * as having multiple resources with many agents, but they are not common. The @@ -665,39 +662,33 @@ const WorkspaceApps: FC = ({ workspace }) => { if (builtinApps.has("vscode")) { buttons.push( - - , + , ); } if (builtinApps.has("vscode_insiders")) { buttons.push( - - , + , ); } @@ -794,40 +785,122 @@ const IconAppLink: FC = ({ app, workspace, agent }) => { ); }; -type BaseIconLinkProps = PropsWithChildren<{ +type VSCodeIconLinkProps = PropsWithChildren<{ + variant: "vscode" | "vscode-insiders"; label: string; - href: string; - isLoading?: boolean; - onClick?: (e: React.MouseEvent) => void; - target?: string; + owner: string; + workspace: string; + agent: string; + folder?: string; }>; +// Generates an API key on click instead of on page load, since +// key generation is a POST request that should only fire when +// the user actually wants to open VS Code. +const VSCodeIconLink: FC = ({ + variant, + label, + owner, + workspace, + agent, + folder, + children, +}) => { + const generateKeyMutation = useMutation({ + mutationFn: () => API.getApiKey(), + onSuccess: ({ key }) => { + // We use a `location.href` here instead of a `navigate` because + // these are protocol-specific links. + location.href = getVSCodeHref(variant, { + owner, + workspace, + token: key, + agent, + folder, + }); + }, + }); + + return ( + { + if (!generateKeyMutation.isPending) { + generateKeyMutation.mutate(); + } + }} + > + {children} + + ); +}; + +type BaseIconLinkCommonProps = PropsWithChildren<{ + label: string; + isLoading?: boolean; +}>; + +type BaseIconLinkAnchorProps = BaseIconLinkCommonProps & { + href: string; + onClick?: (e: React.MouseEvent) => void; + target?: string; +}; + +type BaseIconLinkButtonProps = BaseIconLinkCommonProps & { + href?: never; + onClick: (e: React.MouseEvent) => void; +}; + +type BaseIconLinkProps = BaseIconLinkAnchorProps | BaseIconLinkButtonProps; + const BaseIconLink: FC = ({ - href, isLoading, label, children, - target, - onClick, + ...rest }) => { + const loadingClass = isLoading ? "animate-pulse" : ""; + return ( - + ) : ( + + + )} {label}