Skip to content

Commit 39c9e63

Browse files
committed
[dashboard] allow show options on ws start
1 parent 5570678 commit 39c9e63

File tree

6 files changed

+95
-60
lines changed

6 files changed

+95
-60
lines changed

components/dashboard/src/app/AppRoutes.tsx

+23-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import React, { FunctionComponent, useState } from "react";
7+
import React, { FunctionComponent, useContext, useState } from "react";
88
import { ContextURL, User, Team } from "@gitpod/gitpod-protocol";
99
import SelectIDEModal from "../settings/SelectIDEModal";
1010
import { StartPage, StartPhase } from "../start/StartPage";
@@ -48,6 +48,8 @@ import { Blocked } from "./Blocked";
4848
// TODO: Can we bundle-split/lazy load these like other pages?
4949
import { BlockedRepositories } from "../admin/BlockedRepositories";
5050
import PersonalAccessTokenCreateView from "../settings/PersonalAccessTokensCreateView";
51+
import { StartWorkspaceModalContext } from "../workspaces/start-workspace-modal-context";
52+
import { parseSearchParams } from "../start/CreateWorkspace";
5153

5254
const Setup = React.lazy(() => import(/* webpackPrefetch: true */ "../Setup"));
5355
const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ "../workspaces/Workspaces"));
@@ -94,7 +96,7 @@ type AppRoutesProps = {
9496
};
9597
export const AppRoutes: FunctionComponent<AppRoutesProps> = ({ user, teams }) => {
9698
const hash = getURLHash();
97-
99+
const { startWorkspaceModalProps, setStartWorkspaceModalProps } = useContext(StartWorkspaceModalContext);
98100
const [isWhatsNewShown, setWhatsNewShown] = useState(shouldSeeWhatsNew(user));
99101

100102
// Prefix with `/#referrer` will specify an IDE for workspace
@@ -126,6 +128,19 @@ export const AppRoutes: FunctionComponent<AppRoutesProps> = ({ user, teams }) =>
126128
<SelectIDEModal location="workspace_start" onClose={() => setShowUserIdePreference(false)} />
127129
</StartPage>
128130
);
131+
} else if (new URLSearchParams(window.location.search).has("showOptions")) {
132+
const props = parseSearchParams(window.location.search);
133+
return (
134+
<StartWorkspaceModal
135+
{...{
136+
contextUrl: hash,
137+
ide: props?.ideSettings?.defaultIde,
138+
uselatestIde: props?.ideSettings?.useLatestVersion,
139+
workspaceClass: props.workspaceClass,
140+
onClose: undefined,
141+
}}
142+
/>
143+
);
129144
} else {
130145
// return <div>create workspace yay {hash}</div>;
131146
return <CreateWorkspace contextUrl={hash} />;
@@ -294,7 +309,12 @@ export const AppRoutes: FunctionComponent<AppRoutesProps> = ({ user, teams }) =>
294309
}}
295310
></Route>
296311
</Switch>
297-
<StartWorkspaceModal />
312+
{startWorkspaceModalProps && (
313+
<StartWorkspaceModal
314+
{...startWorkspaceModalProps}
315+
onClose={startWorkspaceModalProps.onClose || (() => setStartWorkspaceModalProps(undefined))}
316+
/>
317+
)}
298318
</div>
299319
</Route>
300320
);

components/dashboard/src/components/RepositoryFinder.tsx

+23-14
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const LOCAL_STORAGE_KEY = "open-in-gitpod-search-data";
1414
interface RepositoryFinderProps {
1515
initialValue?: string;
1616
maxDisplayItems?: number;
17-
setSelection: (selection: string) => void;
17+
setSelection?: (selection: string) => void;
1818
}
1919

2020
function stripOffProtocol(url: string): string {
@@ -67,26 +67,35 @@ export default function RepositoryFinder(props: RepositoryFinderProps) {
6767
[suggestedContextURLs],
6868
);
6969

70+
const element = (
71+
<div className="flex h-12" title="Repository">
72+
<div className="mx-2 my-2">
73+
<img className="w-8 filter-grayscale self-center" src={Repository} alt="logo" />
74+
</div>
75+
<div className="flex-col ml-1 mt-1 flex-grow">
76+
<div className="flex font-semibold text-gray-700">
77+
<div className="text-gray-700 dark:text-gray-300">Context URL</div>
78+
</div>
79+
<div className={"flex text-xs text-gray-500 dark:text-gray-400 font-semibold "}>
80+
{displayContextUrl(props.initialValue) || "Select a repository"}
81+
</div>
82+
</div>
83+
</div>
84+
);
85+
86+
if (!props.setSelection) {
87+
// readonly display value
88+
return element;
89+
}
90+
7091
return (
7192
<DropDown2
7293
getElements={getElements}
7394
expanded={!props.initialValue}
7495
onSelectionChange={props.setSelection}
7596
searchPlaceholder="Paste repository URL or type to find suggestions"
7697
>
77-
<div className="flex h-12" title="Repository">
78-
<div className="mx-2 my-2">
79-
<img className="w-8 filter-grayscale self-center" src={Repository} alt="logo" />
80-
</div>
81-
<div className="flex-col ml-1 mt-1 flex-grow">
82-
<div className="flex font-semibold text-gray-700">
83-
<div className="text-gray-700 dark:text-gray-300">Repository</div>
84-
</div>
85-
<div className={"flex text-xs text-gray-500 dark:text-gray-400 font-semibold "}>
86-
{displayContextUrl(props.initialValue) || "Select a repository"}
87-
</div>
88-
</div>
89-
</div>
98+
{element}
9099
</DropDown2>
91100
);
92101
}

components/dashboard/src/start/CreateWorkspace.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface CreateWorkspaceState {
4040
stillParsing: boolean;
4141
}
4242

43-
function parseSearchParams(search: string): GitpodServer.StartWorkspaceOptions {
43+
export function parseSearchParams(search: string): GitpodServer.StartWorkspaceOptions {
4444
const params = new URLSearchParams(search);
4545
const options: GitpodServer.StartWorkspaceOptions = {};
4646
if (params.has(StartOptions.WORKSPACE_CLASS)) {

components/dashboard/src/workspaces/StartWorkspaceModal.tsx

+34-33
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,29 @@
55
*/
66

77
import { StartOptions } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url";
8-
import { useCallback, useContext, useEffect, useState } from "react";
9-
import { useLocation } from "react-router";
8+
import { useCallback, useContext, useMemo, useState } from "react";
109
import Modal from "../components/Modal";
1110
import RepositoryFinder from "../components/RepositoryFinder";
1211
import SelectIDEComponent from "../components/SelectIDEComponent";
1312
import SelectWorkspaceClassComponent from "../components/SelectWorkspaceClassComponent";
1413
import { UserContext } from "../user-context";
15-
import { StartWorkspaceModalContext } from "./start-workspace-modal-context";
1614

17-
export function StartWorkspaceModal() {
15+
export interface StartWorkspaceModalProps {
16+
uselatestIde?: boolean;
17+
ide?: string;
18+
workspaceClass?: string;
19+
contextUrl?: string;
20+
onClose?: () => void;
21+
}
22+
23+
export function StartWorkspaceModal(props: StartWorkspaceModalProps) {
1824
const { user } = useContext(UserContext);
19-
const { isStartWorkspaceModalVisible, setIsStartWorkspaceModalVisible } = useContext(StartWorkspaceModalContext);
20-
const location = useLocation();
2125
const [useLatestIde, setUseLatestIde] = useState<boolean | undefined>(
22-
!!user?.additionalData?.ideSettings?.useLatestVersion,
26+
props.uselatestIde || !!user?.additionalData?.ideSettings?.useLatestVersion,
2327
);
24-
const [selectedIde, setSelectedIde] = useState(user?.additionalData?.ideSettings?.defaultIde);
25-
const [selectedWsClass, setSelectedWsClass] = useState<string | undefined>();
26-
const [repo, setRepo] = useState<string | undefined>(undefined);
28+
const [selectedIde, setSelectedIde] = useState(props.ide || user?.additionalData?.ideSettings?.defaultIde);
29+
const [selectedWsClass, setSelectedWsClass] = useState<string | undefined>(props.workspaceClass);
30+
const [repo, setRepo] = useState<string | undefined>(props.contextUrl);
2731
const onSelectEditorChange = useCallback(
2832
(ide: string, useLatest: boolean) => {
2933
setSelectedIde(ide);
@@ -52,37 +56,34 @@ export function StartWorkspaceModal() {
5256
return true;
5357
}, [repo, selectedIde, selectedWsClass, useLatestIde]);
5458

55-
// Close the modal on navigation events.
56-
useEffect(() => {
57-
setIsStartWorkspaceModalVisible(false);
58-
}, [location, setIsStartWorkspaceModalVisible]);
59-
60-
useEffect(() => {
61-
// reset state when visibility changes.
62-
setSelectedIde(user?.additionalData?.ideSettings?.defaultIde);
63-
setUseLatestIde(!!user?.additionalData?.ideSettings?.useLatestVersion);
64-
setRepo(undefined);
65-
}, [user, setSelectedIde, setUseLatestIde, isStartWorkspaceModalVisible]);
59+
const buttons = useMemo(() => {
60+
const result = [
61+
<button key="cancel" className="secondary" onClick={props.onClose}>
62+
Cancel
63+
</button>,
64+
<button key="start" className="" onClick={startWorkspace} disabled={!repo || repo.length === 0}>
65+
New Workspace
66+
</button>,
67+
];
68+
if (!props.onClose) {
69+
return result.slice(1, 2);
70+
}
71+
return result;
72+
}, [props.onClose, repo, startWorkspace]);
6673

6774
return (
6875
<Modal
69-
onClose={() => setIsStartWorkspaceModalVisible(false)}
76+
onClose={props.onClose || (() => {})}
77+
closeable={!!props.onClose}
7078
onEnter={startWorkspace}
71-
visible={!!isStartWorkspaceModalVisible}
79+
visible={true}
7280
title="Open in Gitpod"
73-
buttons={[
74-
<button key="cancel" className="secondary" onClick={() => setIsStartWorkspaceModalVisible(false)}>
75-
Cancel
76-
</button>,
77-
<button key="start" className="" onClick={startWorkspace} disabled={!repo || repo.length === 0}>
78-
New Workspace
79-
</button>,
80-
]}
81+
buttons={buttons}
8182
>
8283
<div className="-mx-6 px-6">
83-
<div className="text-xs text-gray-500">Select a repository and configure workspace options.</div>
84+
<div className="text-xs text-gray-500">Start a new workspace with the following options.</div>
8485
<div className="pt-3">
85-
<RepositoryFinder setSelection={setRepo} initialValue={repo} />
86+
<RepositoryFinder setSelection={props.contextUrl ? undefined : setRepo} initialValue={repo} />
8687
</div>
8788
<div className="pt-3">
8889
<SelectIDEComponent

components/dashboard/src/workspaces/Workspaces.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default function () {
4545
const [workspaceModel, setWorkspaceModel] = useState<WorkspaceModel>();
4646
const [showInactive, setShowInactive] = useState<boolean>();
4747
const [deleteModalVisible, setDeleteModalVisible] = useState<boolean>();
48-
const { setIsStartWorkspaceModalVisible } = useContext(StartWorkspaceModalContext);
48+
const { setStartWorkspaceModalProps } = useContext(StartWorkspaceModalContext);
4949

5050
useEffect(() => {
5151
(async () => {
@@ -138,7 +138,7 @@ export default function () {
138138
]}
139139
/>
140140
</div>
141-
<button onClick={() => setIsStartWorkspaceModalVisible(true)} className="ml-2">
141+
<button onClick={() => setStartWorkspaceModalProps({})} className="ml-2">
142142
New Workspace{" "}
143143
<span className="opacity-60 hidden md:inline">{StartWorkspaceModalKeyBinding}</span>
144144
</button>
@@ -238,7 +238,7 @@ export default function () {
238238
</a>
239239
</div>
240240
<span>
241-
<button onClick={() => setIsStartWorkspaceModalVisible(true)}>
241+
<button onClick={() => setStartWorkspaceModalProps({})}>
242242
New Workspace{" "}
243243
<span className="opacity-60 hidden md:inline">
244244
{StartWorkspaceModalKeyBinding}

components/dashboard/src/workspaces/start-workspace-modal-context.tsx

+11-6
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@
55
*/
66

77
import React, { createContext, useEffect, useState } from "react";
8+
import { StartWorkspaceModalProps } from "./StartWorkspaceModal";
89

910
export const StartWorkspaceModalContext = createContext<{
10-
isStartWorkspaceModalVisible?: boolean;
11-
setIsStartWorkspaceModalVisible: React.Dispatch<boolean>;
11+
startWorkspaceModalProps?: StartWorkspaceModalProps;
12+
setStartWorkspaceModalProps: React.Dispatch<StartWorkspaceModalProps | undefined>;
1213
}>({
13-
setIsStartWorkspaceModalVisible: () => null,
14+
setStartWorkspaceModalProps: () => null,
1415
});
1516

1617
export const StartWorkspaceModalContextProvider: React.FC = ({ children }) => {
17-
const [isStartWorkspaceModalVisible, setIsStartWorkspaceModalVisible] = useState<boolean>(false);
18+
const [startWorkspaceModalProps, setStartWorkspaceModalProps] = useState<StartWorkspaceModalProps | undefined>(
19+
undefined,
20+
);
1821

1922
useEffect(() => {
2023
const onKeyDown = (event: KeyboardEvent) => {
2124
if ((event.metaKey || event.ctrlKey) && event.key === "o") {
2225
event.preventDefault();
23-
setIsStartWorkspaceModalVisible(true);
26+
setStartWorkspaceModalProps({
27+
onClose: () => setStartWorkspaceModalProps(undefined),
28+
});
2429
}
2530
};
2631
window.addEventListener("keydown", onKeyDown);
@@ -30,7 +35,7 @@ export const StartWorkspaceModalContextProvider: React.FC = ({ children }) => {
3035
}, []);
3136

3237
return (
33-
<StartWorkspaceModalContext.Provider value={{ isStartWorkspaceModalVisible, setIsStartWorkspaceModalVisible }}>
38+
<StartWorkspaceModalContext.Provider value={{ startWorkspaceModalProps, setStartWorkspaceModalProps }}>
3439
{children}
3540
</StartWorkspaceModalContext.Provider>
3641
);

0 commit comments

Comments
 (0)