Skip to content

Commit 0301b90

Browse files
[dashboard] Fix Arc favourites (#20466)
* [dashboard] Fix Arc favourites * 💄 * Fix banner <> redirect race
1 parent 4c25f42 commit 0301b90

File tree

2 files changed

+53
-7
lines changed

2 files changed

+53
-7
lines changed

components/dashboard/src/components/podkit/buttons/LinkButton.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,30 @@
66

77
import { Link } from "react-router-dom";
88
import { Button, ButtonProps } from "@podkit/buttons/Button";
9-
import React from "react";
9+
import { forwardRef, HTMLAttributeAnchorTarget } from "react";
1010

1111
export interface LinkButtonProps extends ButtonProps {
1212
asChild?: false;
1313
href: string;
14+
target?: HTMLAttributeAnchorTarget;
1415
isExternalUrl?: boolean;
1516
}
1617

1718
/**
1819
* A HTML anchor element styled as a button.
1920
*/
20-
export const LinkButton = React.forwardRef<HTMLButtonElement, LinkButtonProps>(
21-
({ asChild, children, href, ...props }, ref) => {
21+
export const LinkButton = forwardRef<HTMLButtonElement, LinkButtonProps>(
22+
({ asChild, children, href, target, ...props }, ref) => {
2223
return (
2324
<Button ref={ref} {...props} asChild>
2425
{props.isExternalUrl ? (
25-
<a href={href} target="_blank" rel="noreferrer">
26+
<a href={href} target={target ?? "_blank"} rel="noreferrer">
2627
{children}
2728
</a>
2829
) : (
29-
<Link to={href}>{children}</Link>
30+
<Link to={href} target={target}>
31+
{children}
32+
</Link>
3033
)}
3134
</Button>
3235
);

components/dashboard/src/start/StartWorkspace.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
import { PartialMessage } from "@bufbuild/protobuf";
3737
import { trackEvent } from "../Analytics";
3838
import { fromWorkspaceName } from "../workspaces/RenameWorkspaceModal";
39+
import { LinkButton } from "@podkit/buttons/LinkButton";
3940

4041
const sessionId = v4();
4142

@@ -102,6 +103,14 @@ export interface StartWorkspaceState {
102103
ideOptions?: IDEOptions;
103104
isSSHModalVisible?: boolean;
104105
ownerToken?: string;
106+
/**
107+
* Set to prevent multiple redirects to the same URL when the User Agent ignores our wish to open links in the same tab (by setting window.location.href).
108+
*/
109+
redirected?: boolean;
110+
/**
111+
* Determines whether `redirected` has been `true` for long enough to display our "new tab" info banner without racing with same-tab redirection in regular setups
112+
*/
113+
showRedirectMessage?: boolean;
105114
}
106115

107116
// TODO: use Function Components
@@ -183,7 +192,7 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
183192
this.toDispose.dispose();
184193
}
185194

186-
componentDidUpdate(prevPros: StartWorkspaceProps, prevState: StartWorkspaceState) {
195+
componentDidUpdate(_prevProps: StartWorkspaceProps, prevState: StartWorkspaceState) {
187196
const newPhase = this.state?.workspace?.status?.phase?.name;
188197
const oldPhase = prevState.workspace?.status?.phase?.name;
189198
const type = this.state.workspace?.spec?.type === WorkspaceSpec_WorkspaceType.PREBUILD ? "prebuild" : "regular";
@@ -458,11 +467,20 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
458467
}
459468

460469
redirectTo(url: string) {
470+
if (this.state.redirected) {
471+
console.info("Prevented another redirect", { url });
472+
return;
473+
}
461474
if (this.props.runsInIFrame) {
462475
this.ideFrontendService?.relocate(url);
463476
} else {
464477
window.location.href = url;
465478
}
479+
480+
this.setState({ redirected: true });
481+
setTimeout(() => {
482+
this.setState({ showRedirectMessage: true });
483+
}, 2000);
466484
}
467485

468486
private openDesktopLink(link: string) {
@@ -503,7 +521,7 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
503521
return <ImageBuildView workspaceId={this.state.workspace.id} />;
504522

505523
// Pending means the workspace does not yet consume resources in the cluster, but rather is looking for
506-
// some space within the cluster. If for example the cluster needs to scale up to accomodate the
524+
// some space within the cluster. If for example the cluster needs to scale up to accommodate the
507525
// workspace, the workspace will be in Pending state until that happened.
508526
case WorkspacePhase_Phase.PENDING:
509527
phase = StartPhase.Preparing;
@@ -746,6 +764,7 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
746764
);
747765
break;
748766
}
767+
749768
return (
750769
<StartPage
751770
phase={phase}
@@ -755,6 +774,30 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
755774
workspaceId={this.props.workspaceId}
756775
>
757776
{statusMessage}
777+
{this.state.showRedirectMessage && (
778+
<>
779+
<Alert type="info" className="mt-4 w-112">
780+
We redirected you to your workspace, but your browser probably opened it in another tab.
781+
</Alert>
782+
783+
<div className="mt-4 justify-center flex space-x-2">
784+
<LinkButton href={gitpodHostUrl.asWorkspacePage().toString()} target="_self" isExternalUrl>
785+
Go to Dashboard
786+
</LinkButton>
787+
{this.state.workspace?.status?.workspaceUrl &&
788+
this.state.workspace.status.phase?.name === WorkspacePhase_Phase.RUNNING && (
789+
<LinkButton
790+
variant={"secondary"}
791+
href={this.state.workspace.status.workspaceUrl}
792+
target="_self"
793+
isExternalUrl
794+
>
795+
Re-open Workspace
796+
</LinkButton>
797+
)}
798+
</div>
799+
</>
800+
)}
758801
</StartPage>
759802
);
760803
}

0 commit comments

Comments
 (0)