Skip to content

Commit 0c08926

Browse files
filiptronicekgtsioliseasyCZ
authored
Replace <SelectIDE /> with <SelectIDEComponent /> (#16847)
* Replace `<SelectIDE />` with `<SelectIDEComponent />` * The latest checkbox * Don't show stable IDEs when latest is selected * Limit width * Enlarge * Remove `<SelectIDE />` * Back, back to school again * Dead code * Fix width * Use `width` instead of `max-width` * Remove unused import * Show JB beta notice * Add a margin to the selector * Remove custom modal width Co-authored-by: George Tsiolis <[email protected]> * Finish off the age-old todo :) * Remove `<SelectIDEModal />` * retest * Remove unused code * use `useUpdateCurrentUserMutation` --------- Co-authored-by: George Tsiolis <[email protected]> Co-authored-by: Milan Pavlik <[email protected]>
1 parent 260497b commit 0c08926

File tree

7 files changed

+92
-276
lines changed

7 files changed

+92
-276
lines changed

components/dashboard/src/app/AppRoutes.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { ContextURL, User } from "@gitpod/gitpod-protocol";
7+
import { User } from "@gitpod/gitpod-protocol";
88
import React, { useContext, useState } from "react";
99
import { Redirect, Route, Switch, useLocation } from "react-router";
1010
import { AppNotifications } from "../AppNotifications";
1111
import Menu from "../menu/Menu";
1212
import OAuthClientApproval from "../OauthClientApproval";
1313
import { projectsPathInstallGitHubApp, projectsPathNew } from "../projects/projects.routes";
14-
import { StartPage, StartPhase } from "../start/StartPage";
1514
import { parseProps } from "../start/StartWorkspace";
16-
import SelectIDEModal from "../user-settings/SelectIDEModal";
1715
import {
1816
settingsPathAccount,
1917
settingsPathBilling,
@@ -137,16 +135,7 @@ export const AppRoutes = () => {
137135
// TODO: Try and encapsulate this in a route for "/" (check for hash in route component, render or redirect accordingly)
138136
const isCreation = location.pathname === "/" && hash !== "";
139137
if (isCreation) {
140-
// Prefix with `/#referrer` will specify an IDE for workspace
141-
// After selection is saved, user will be updated, and this condition will be false
142-
const showIDESelection = User.isOnboardingUser(user) && !hash.startsWith(ContextURL.REFERRER_PREFIX);
143-
if (showIDESelection) {
144-
return (
145-
<StartPage phase={StartPhase.Checking}>
146-
<SelectIDEModal location="workspace_start" />
147-
</StartPage>
148-
);
149-
} else if (new URLSearchParams(location.search).has("showOptions") || newCreateWsPage) {
138+
if (new URLSearchParams(location.search).has("showOptions") || newCreateWsPage) {
150139
return <Redirect to={"/new" + location.pathname + location.search + location.hash} />;
151140
} else {
152141
return <CreateWorkspace contextUrl={hash} />;

components/dashboard/src/components/SelectIDEComponent.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ export default function SelectIDEComponent(props: SelectIDEComponentProps) {
3232
for (const ide of options.filter((ide) =>
3333
`${ide.label}${ide.title}${ide.notes}${ide.id}`.toLowerCase().includes(search.toLowerCase()),
3434
)) {
35-
result.push({
36-
id: ide.id,
37-
element: <IdeOptionElementInDropDown option={ide} useLatest={false} />,
38-
isSelectable: true,
39-
});
40-
if (ide.latestImage) {
35+
if (!props.useLatest) {
36+
result.push({
37+
id: ide.id,
38+
element: <IdeOptionElementInDropDown option={ide} useLatest={false} />,
39+
isSelectable: true,
40+
});
41+
} else if (ide.latestImage) {
4142
result.push({
4243
id: ide.id + "-latest",
4344
element: <IdeOptionElementInDropDown option={ide} useLatest={true} />,
@@ -47,7 +48,7 @@ export default function SelectIDEComponent(props: SelectIDEComponentProps) {
4748
}
4849
return result;
4950
},
50-
[ideOptions],
51+
[ideOptions, props.useLatest],
5152
);
5253
const internalOnSelectionChange = (id: string) => {
5354
const { ide, useLatest } = parseId(id);

components/dashboard/src/user-settings/Preferences.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import { useCallback, useContext, useState } from "react";
88
import { getGitpodService } from "../service/service";
99
import { UserContext } from "../user-context";
1010
import { trackEvent } from "../Analytics";
11-
import SelectIDE from "./SelectIDE";
1211
import { PageWithSettingsSubMenu } from "./PageWithSettingsSubMenu";
1312
import { ThemeSelector } from "../components/ThemeSelector";
1413
import Alert from "../components/Alert";
1514
import { Link } from "react-router-dom";
1615
import { Heading2, Subheading } from "../components/typography/headings";
1716
import { useUserMaySetTimeout } from "../data/current-user/may-set-timeout-query";
1817
import { Button } from "../components/Button";
18+
import SelectIDE from "./SelectIDE";
19+
20+
export type IDEChangedTrackLocation = "workspace_list" | "workspace_start" | "preferences";
1921

2022
export default function Preferences() {
2123
const { user, setUser } = useContext(UserContext);
@@ -74,7 +76,7 @@ export default function Preferences() {
7476
Learn more
7577
</a>
7678
</Subheading>
77-
<SelectIDE location="preferences" />
79+
<SelectIDE updateUserContext={false} location="preferences" />
7880

7981
<ThemeSelector className="mt-12" />
8082

Lines changed: 77 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,123 @@
11
/**
2-
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
33
* Licensed under the GNU Affero General Public License (AGPL).
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { IDEOption, IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol";
87
import { useContext, useEffect, useState } from "react";
9-
import InfoBox from "../components/InfoBox";
10-
import SelectableCardSolid from "../components/SelectableCardSolid";
11-
import Tooltip from "../components/Tooltip";
12-
import { getGitpodService } from "../service/service";
138
import { UserContext } from "../user-context";
149
import CheckBox from "../components/CheckBox";
1510
import { User } from "@gitpod/gitpod-protocol";
11+
import SelectIDEComponent from "../components/SelectIDEComponent";
1612
import PillLabel from "../components/PillLabel";
13+
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";
1714

1815
export type IDEChangedTrackLocation = "workspace_list" | "workspace_start" | "preferences";
1916
interface SelectIDEProps {
2017
updateUserContext?: boolean;
2118
location: IDEChangedTrackLocation;
2219
}
2320

24-
export const updateUserIDEInfo = async (
25-
user: User,
26-
selectedIde: string,
27-
useLatestVersion: boolean,
28-
location: IDEChangedTrackLocation,
29-
) => {
30-
const additionalData = user?.additionalData ?? {};
31-
const settings = additionalData.ideSettings ?? {};
32-
settings.settingVersion = "2.0";
33-
settings.defaultIde = selectedIde;
34-
settings.useLatestVersion = useLatestVersion;
35-
additionalData.ideSettings = settings;
36-
getGitpodService()
37-
.server.trackEvent({
38-
event: "ide_configuration_changed",
39-
properties: {
40-
...settings,
41-
location,
42-
},
43-
})
44-
.then()
45-
.catch(console.error);
46-
return getGitpodService().server.updateLoggedInUser({ additionalData });
47-
};
48-
4921
export default function SelectIDE(props: SelectIDEProps) {
5022
const { user, setUser } = useContext(UserContext);
23+
const updateUser = useUpdateCurrentUserMutation();
5124

5225
// Only exec once when we access this component
5326
useEffect(() => {
5427
user && User.migrationIDESettings(user);
5528
// eslint-disable-next-line react-hooks/exhaustive-deps
5629
}, []);
5730

58-
const actualUpdateUserIDEInfo = async (user: User, selectedIde: string, useLatestVersion: boolean) => {
59-
const newUserData = await updateUserIDEInfo(user, selectedIde, useLatestVersion, props.location);
31+
const [defaultIde, setDefaultIde] = useState<string>(user?.additionalData?.ideSettings?.defaultIde || "code");
32+
const [useLatestVersion, setUseLatestVersion] = useState<boolean>(
33+
user?.additionalData?.ideSettings?.useLatestVersion ?? false,
34+
);
35+
36+
const actualUpdateUserIDEInfo = async (selectedIde: string, useLatestVersion: boolean) => {
37+
const additionalData = user?.additionalData ?? {};
38+
const settings = additionalData.ideSettings ?? {};
39+
settings.settingVersion = "2.0";
40+
settings.defaultIde = selectedIde;
41+
settings.useLatestVersion = useLatestVersion;
42+
additionalData.ideSettings = settings;
43+
44+
const newUserData = await updateUser.mutateAsync({ additionalData });
6045
props.updateUserContext && setUser({ ...newUserData });
6146
};
6247

63-
const [defaultIde, setDefaultIde] = useState<string>(user?.additionalData?.ideSettings?.defaultIde || "code");
6448
const actuallySetDefaultIde = async (value: string) => {
65-
await actualUpdateUserIDEInfo(user!, value, useLatestVersion);
49+
await actualUpdateUserIDEInfo(value, useLatestVersion);
6650
setDefaultIde(value);
6751
};
6852

69-
const [useLatestVersion, setUseLatestVersion] = useState<boolean>(
70-
user?.additionalData?.ideSettings?.useLatestVersion ?? false,
71-
);
7253
const actuallySetUseLatestVersion = async (value: boolean) => {
73-
await actualUpdateUserIDEInfo(user!, defaultIde, value);
54+
await actualUpdateUserIDEInfo(defaultIde, value);
7455
setUseLatestVersion(value);
7556
};
7657

77-
const [ideOptions, setIdeOptions] = useState<IDEOptions | undefined>(undefined);
78-
useEffect(() => {
79-
(async () => {
80-
setIdeOptions(await getGitpodService().server.getIDEOptions());
81-
})();
82-
}, []);
83-
84-
const allIdeOptions = ideOptions && orderedIdeOptions(ideOptions);
58+
//todo(ft): find a better way to group IDEs by vendor
59+
const shouldShowJetbrainsNotice = !["code", "code-desktop"].includes(defaultIde); // a really hacky way to get just JetBrains IDEs
8560

8661
return (
8762
<>
88-
{ideOptions && (
89-
<>
90-
{allIdeOptions && (
91-
<>
92-
<div className={`my-4 gap-3 flex flex-wrap max-w-3xl`}>
93-
{allIdeOptions.map(([id, option]) => {
94-
const selected = defaultIde === id;
95-
const version = useLatestVersion ? option.latestImageVersion : option.imageVersion;
96-
const onSelect = () => actuallySetDefaultIde(id);
97-
return renderIdeOption(option, selected, version, onSelect);
98-
})}
99-
</div>
100-
{ideOptions.options[defaultIde]?.notes && (
101-
<InfoBox className="my-5 max-w-2xl">
102-
<ul>
103-
{ideOptions.options[defaultIde].notes?.map((x, idx) => (
104-
<li className={idx > 0 ? "mt-2" : ""}>{x}</li>
105-
))}
106-
</ul>
107-
</InfoBox>
108-
)}
109-
110-
<p className="text-left w-full text-gray-400 dark:text-gray-500">
111-
<strong>JetBrains </strong> integration is currently in{" "}
112-
<PillLabel type="warn" className="font-semibold mt-2 ml-0 py-0.5 px-1 self-center">
113-
<a href="https://www.gitpod.io/docs/references/gitpod-releases">
114-
<span className="text-xs">Beta</span>
115-
</a>
116-
</PillLabel>
117-
&nbsp;&middot;&nbsp;
118-
<a
119-
href="https://github.com/gitpod-io/gitpod/issues/6576"
120-
target="_blank"
121-
rel="noopener noreferrer"
122-
className="gp-link"
123-
>
124-
Send feedback
125-
</a>
126-
</p>
127-
</>
128-
)}
129-
<CheckBox
130-
title="Latest Release (Unstable)"
131-
desc={
132-
<span>
133-
Use the latest version for each editor.{" "}
134-
<a
135-
className="gp-link"
136-
target="_blank"
137-
href="https://code.visualstudio.com/blogs/2016/02/01/introducing_insiders_build"
138-
rel="noreferrer"
139-
>
140-
Insiders
141-
</a>{" "}
142-
for VS Code,{" "}
143-
<a
144-
className="gp-link"
145-
target="_blank"
146-
href="https://www.jetbrains.com/resources/eap/"
147-
rel="noreferrer"
148-
>
149-
EAP
150-
</a>{" "}
151-
for JetBrains IDEs.
152-
</span>
153-
}
154-
checked={useLatestVersion}
155-
onChange={(e) => actuallySetUseLatestVersion(e.target.checked)}
156-
/>
157-
</>
158-
)}
159-
</>
160-
);
161-
}
162-
163-
function orderedIdeOptions(ideOptions: IDEOptions) {
164-
// TODO: Maybe convert orderKey to number before sort?
165-
return Object.entries(ideOptions.options)
166-
.filter(([_, x]) => !x.hidden)
167-
.sort((a, b) => {
168-
const keyA = a[1].orderKey || a[0];
169-
const keyB = b[1].orderKey || b[0];
170-
return keyA.localeCompare(keyB);
171-
});
172-
}
173-
174-
function renderIdeOption(
175-
option: IDEOption,
176-
selected: boolean,
177-
version: IDEOption["imageVersion"],
178-
onSelect: () => void,
179-
): JSX.Element {
180-
const shouldShowOptionType = option.type !== "desktop" || option.title === "VS Code"; // Force show of "Desktop" in the list for VS Code Desktop
181-
const card = (
182-
<SelectableCardSolid className="w-36 h-44" title={option.title} selected={selected} onClick={onSelect}>
183-
{version ? (
184-
<span
185-
className={`font-normal font-mono text-xs ${
186-
selected
187-
? "text-gray-100 dark:text-gray-600"
188-
: "text-gray-400 contrast-more:text-gray-600 dark:text-gray-500"
189-
} pl-1 self-start`}
190-
title="The IDE's current version on Gitpod"
191-
>
192-
{version}
193-
</span>
194-
) : (
195-
<span
196-
style={{
197-
minHeight: "1rem",
63+
<div className="w-112 my-4">
64+
<SelectIDEComponent
65+
onSelectionChange={async (ide) => {
66+
await actuallySetDefaultIde(ide);
19867
}}
199-
></span>
200-
)}
201-
<div className="flex justify-center mt-3 mb-2">
202-
<img className="w-16 filter-grayscale self-center" src={option.logo} alt="logo" />
68+
selectedIdeOption={defaultIde}
69+
useLatest={useLatestVersion}
70+
/>
20371
</div>
204-
{shouldShowOptionType ? (
205-
<PillLabel type="warn" className="place-self-start py-0.5 my-2 flex">
206-
<span className="text-xs capitalize">{option.type}</span>
207-
</PillLabel>
208-
) : (
209-
option.label && (
210-
<PillLabel type="neutral" className="place-self-start py-0.5 my-2 flex">
211-
<span className="text-xs normal-case font-medium">{option.label}</span>
72+
73+
{shouldShowJetbrainsNotice && (
74+
<p className="text-left w-full text-gray-400 dark:text-gray-500">
75+
<strong>JetBrains </strong> integration is currently in{" "}
76+
<PillLabel type="warn" className="font-semibold mt-2 ml-0 py-0.5 px-1 self-center">
77+
<a href="https://www.gitpod.io/docs/references/gitpod-releases">
78+
<span className="text-xs">Beta</span>
79+
</a>
21280
</PillLabel>
213-
)
81+
&nbsp;&middot;&nbsp;
82+
<a
83+
href="https://github.com/gitpod-io/gitpod/issues/6576"
84+
target="_blank"
85+
rel="noopener noreferrer"
86+
className="gp-link"
87+
>
88+
Send feedback
89+
</a>
90+
</p>
21491
)}
215-
</SelectableCardSolid>
216-
);
21792

218-
if (option.tooltip) {
219-
return <Tooltip content={option.tooltip}>{card}</Tooltip>;
220-
}
221-
return card;
93+
<CheckBox
94+
title="Latest Release (Unstable)"
95+
desc={
96+
<span>
97+
Use the latest version for each editor.{" "}
98+
<a
99+
className="gp-link"
100+
target="_blank"
101+
href="https://code.visualstudio.com/blogs/2016/02/01/introducing_insiders_build"
102+
rel="noreferrer"
103+
>
104+
Insiders
105+
</a>{" "}
106+
for VS Code,{" "}
107+
<a
108+
className="gp-link"
109+
target="_blank"
110+
href="https://www.jetbrains.com/resources/eap/"
111+
rel="noreferrer"
112+
>
113+
EAP
114+
</a>{" "}
115+
for JetBrains IDEs.
116+
</span>
117+
}
118+
checked={useLatestVersion}
119+
onChange={(e) => actuallySetUseLatestVersion(e.target.checked)}
120+
/>
121+
</>
122+
);
222123
}

0 commit comments

Comments
 (0)