Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 111 additions & 38 deletions site/src/pages/WorkspacesPage/WorkspacesTable.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -629,9 +629,6 @@ type WorkspaceAppsProps = {
};

const WorkspaceApps: FC<WorkspaceAppsProps> = ({ 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
Expand Down Expand Up @@ -665,39 +662,33 @@ const WorkspaceApps: FC<WorkspaceAppsProps> = ({ workspace }) => {

if (builtinApps.has("vscode")) {
buttons.push(
<BaseIconLink
<VSCodeIconLink
key="vscode"
isLoading={!token}
variant="vscode"
label="Open VSCode"
href={getVSCodeHref("vscode", {
owner: workspace.owner_name,
workspace: workspace.name,
agent: agent.name,
token: token ?? "",
folder: agent.expanded_directory,
})}
owner={workspace.owner_name}
workspace={workspace.name}
agent={agent.name}
folder={agent.expanded_directory}
>
<VSCodeIcon />
</BaseIconLink>,
</VSCodeIconLink>,
);
}

if (builtinApps.has("vscode_insiders")) {
buttons.push(
<BaseIconLink
<VSCodeIconLink
key="vscode-insiders"
variant="vscode-insiders"
label="Open VSCode Insiders"
isLoading={!token}
href={getVSCodeHref("vscode-insiders", {
owner: workspace.owner_name,
workspace: workspace.name,
agent: agent.name,
token: token ?? "",
folder: agent.expanded_directory,
})}
owner={workspace.owner_name}
workspace={workspace.name}
agent={agent.name}
folder={agent.expanded_directory}
>
<VSCodeInsidersIcon />
</BaseIconLink>,
</VSCodeIconLink>,
);
}

Expand Down Expand Up @@ -794,40 +785,122 @@ const IconAppLink: FC<IconAppLinkProps> = ({ app, workspace, agent }) => {
);
};

type BaseIconLinkProps = PropsWithChildren<{
type VSCodeIconLinkProps = PropsWithChildren<{
variant: "vscode" | "vscode-insiders";
label: 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<VSCodeIconLinkProps> = ({
variant,
label,
owner,
workspace,
agent,
folder,
children,
}) => {
const generateKeyMutation = useMutation({
mutationFn: () => API.getApiKey(),
Comment thread
jakehwll marked this conversation as resolved.
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,
});
},
});
Comment on lines +809 to +822

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: future work will require making multiple useMutation({ mutationFn: () => API.getApiKey() }). Thoughts on creating a helper for that now?


return (
<BaseIconLink
label={label}
isLoading={generateKeyMutation.isPending}
onClick={() => {
if (!generateKeyMutation.isPending) {
generateKeyMutation.mutate();
}
}}
>
{children}
</BaseIconLink>
);
};

type BaseIconLinkCommonProps = PropsWithChildren<{
label: string;
href: string;
isLoading?: boolean;
}>;

type BaseIconLinkAnchorProps = BaseIconLinkCommonProps & {
href: string;
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
target?: string;
}>;
};

type BaseIconLinkButtonProps = BaseIconLinkCommonProps & {
href?: never;
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
};

type BaseIconLinkProps = BaseIconLinkAnchorProps | BaseIconLinkButtonProps;

const BaseIconLink: FC<BaseIconLinkProps> = ({
href,
isLoading,
label,
children,
target,
onClick,
...rest
}) => {
const loadingClass = isLoading ? "animate-pulse" : "";

return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" size="icon-lg" asChild>
<a
target={target}
className={isLoading ? "animate-pulse" : ""}
href={href}
{rest.href !== undefined ? (
<Button
variant="outline"
size="icon-lg"
asChild
disabled={isLoading}
>
<a
target={rest.target}
className={loadingClass}
Comment thread
jeremyruppel marked this conversation as resolved.
href={rest.href}
onClick={(e) => {
e.stopPropagation();
rest.onClick?.(e);
}}
>
{children}
<span className="sr-only">{label}</span>
</a>
</Button>
) : (
<Button
variant="outline"
size="icon-lg"
className={loadingClass}
onClick={(e) => {
e.stopPropagation();
onClick?.(e);
rest.onClick(e);
}}
disabled={isLoading}
>
{children}
<span className="sr-only">{label}</span>
</a>
</Button>
</Button>
)}
</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
Expand Down
Loading