Skip to content

Commit 5aadfa1

Browse files
committed
[dashboard,server] switch ide version with one toggle
1 parent a8d07cb commit 5aadfa1

File tree

9 files changed

+481
-173
lines changed

9 files changed

+481
-173
lines changed

chart/templates/server-ide-configmap.yaml

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,10 @@ options:
4646
orderKey: "00"
4747
title: "VS Code"
4848
type: "browser"
49+
label: "Browser"
4950
logo: "https://ide.{{ $.Values.hostname }}/image/ide-logo/vscode.svg"
5051
image: {{ (include "stable-image-full" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.codeImage)) }}
51-
code-latest:
52-
orderKey: "01"
53-
title: "VS Code"
54-
type: "browser"
55-
logo: "https://ide.{{ $.Values.hostname }}/image/ide-logo/vscodeInsiders.svg"
56-
tooltip: "Early access version, still subject to testing."
57-
label: "Insiders"
58-
image: {{ (include "insider-image-full" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.codeImage)) }}
59-
resolveImageDigest: true
52+
latestImage: {{ (include "insider-image-full" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.codeImage)) }}
6053

6154
# Desktop IDEs
6255
code-desktop:
@@ -65,14 +58,7 @@ options:
6558
type: "desktop"
6659
logo: "https://ide.{{ $.Values.hostname }}/image/ide-logo/vscode.svg"
6760
image: {{ (include "gitpod.comp.imageFull" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.desktopIdeImages.codeDesktop)) }}
68-
code-desktop-insiders:
69-
orderKey: "03"
70-
title: "VS Code"
71-
type: "desktop"
72-
logo: "https://ide.{{ $.Values.hostname }}/image/ide-logo/vscodeInsiders.svg"
73-
tooltip: "Visual Studio Code Insiders for early adopters."
74-
label: "Insiders"
75-
image: {{ (include "gitpod.comp.imageFull" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.desktopIdeImages.codeDesktopInsiders)) }}
61+
latestImage: {{ (include "gitpod.comp.imageFull" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.desktopIdeImages.codeDesktopInsiders)) }}
7662
intellij:
7763
orderKey: "04"
7864
title: "IntelliJ IDEA"
@@ -113,8 +99,8 @@ clients:
11399
"If you don't see an open dialog in your browser, make sure you have <a target='_blank' class='gp-link' href='https://code.visualstudio.com/download'>VS Code</a> installed on your machine, and then click <b>${OPEN_LINK_LABEL}</b> below.",
114100
]
115101
vscode-insiders:
116-
defaultDesktopIDE: "code-desktop-insiders"
117-
desktopIDEs: ["code-desktop-insiders"]
102+
defaultDesktopIDE: "code-desktop"
103+
desktopIDEs: ["code-desktop"]
118104
installationSteps: [
119105
"If you don't see an open dialog in your browser, make sure you have <a target='_blank' class='gp-link' href='https://code.visualstudio.com/insiders'>VS Code Insiders</a> installed on your machine, and then click <b>${OPEN_LINK_LABEL}</b> below.",
120106
]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
export interface SelectableCardSolidProps {
8+
title: string;
9+
selected: boolean;
10+
className?: string;
11+
onClick: () => void;
12+
children?: React.ReactNode;
13+
}
14+
15+
function SelectableCardSolid(props: SelectableCardSolidProps) {
16+
return (
17+
<div
18+
className={`rounded-xl px-3 py-3 flex flex-col cursor-pointer group transition ease-in-out ${
19+
props.selected
20+
? "bg-gray-800 dark:bg-gray-100"
21+
: "bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700"
22+
} ${props.className || ""}`}
23+
onClick={props.onClick}
24+
>
25+
<div className="flex items-center">
26+
<p
27+
className={`w-full pl-1 text-base font-semibold truncate ${
28+
props.selected ? "text-gray-100 dark:text-gray-600" : "text-gray-600 dark:text-gray-500"
29+
}`}
30+
title={props.title}
31+
>
32+
{props.title}
33+
</p>
34+
<input className="opacity-0" type="radio" checked={props.selected} />
35+
</div>
36+
{props.children}
37+
</div>
38+
);
39+
}
40+
41+
export default SelectableCardSolid;

components/dashboard/src/settings/Preferences.tsx

Lines changed: 75 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,62 @@ import InfoBox from "../components/InfoBox";
1010
import { PageWithSubMenu } from "../components/PageWithSubMenu";
1111
import PillLabel from "../components/PillLabel";
1212
import SelectableCard from "../components/SelectableCard";
13+
import SelectableCardSolid from "../components/SelectableCardSolid";
1314
import Tooltip from "../components/Tooltip";
1415
import { getGitpodService } from "../service/service";
1516
import { ThemeContext } from "../theme-context";
1617
import { UserContext } from "../user-context";
1718
import getSettingsMenu from "./settings-menu";
18-
import IDENone from "../icons/IDENone.svg";
19-
import IDENoneDark from "../icons/IDENoneDark.svg";
20-
import CheckBox from "../components/CheckBox";
2119
import { trackEvent } from "../Analytics";
2220
import { PaymentContext } from "../payment-context";
21+
import CheckBox from "../components/CheckBox";
22+
import { IDESettings } from "@gitpod/gitpod-protocol";
2323

2424
type Theme = "light" | "dark" | "system";
2525

26-
const DesktopNoneId = "none";
27-
const DesktopNone: IDEOption = {
28-
image: "",
29-
logo: IDENone,
30-
orderKey: "-1",
31-
title: "None",
32-
type: "desktop",
33-
};
34-
3526
export default function Preferences() {
3627
const { user } = useContext(UserContext);
3728
const { showPaymentUI } = useContext(PaymentContext);
38-
const { setIsDark, isDark } = useContext(ThemeContext);
29+
const { setIsDark } = useContext(ThemeContext);
3930

40-
const updateUserIDEInfo = async (defaultDesktopIde: string, defaultIde: string, useLatestVersion: boolean) => {
41-
const useDesktopIde = defaultDesktopIde !== DesktopNoneId;
42-
const desktopIde = useDesktopIde ? defaultDesktopIde : undefined;
31+
const migrationIDESettings = () => {
32+
if (!user?.additionalData?.ideSettings || user.additionalData.ideSettings.settingVersion === "2.0") {
33+
return;
34+
}
35+
const newIDESettings: IDESettings = {
36+
settingVersion: "2.0",
37+
};
38+
const ideSettings = user.additionalData.ideSettings;
39+
if (ideSettings.useDesktopIde) {
40+
if (ideSettings.defaultDesktopIde === "code-desktop") {
41+
newIDESettings.defaultIde = "code-desktop";
42+
} else if (ideSettings.defaultDesktopIde === "code-desktop-insiders") {
43+
newIDESettings.defaultIde = "code-desktop";
44+
newIDESettings.useLatestVersion = true;
45+
} else {
46+
newIDESettings.defaultIde = ideSettings.defaultDesktopIde;
47+
newIDESettings.useLatestVersion = ideSettings.useLatestVersion;
48+
}
49+
} else {
50+
const useLatest = ideSettings.defaultIde === "code-latest";
51+
newIDESettings.defaultIde = "code";
52+
newIDESettings.useLatestVersion = useLatest;
53+
}
54+
user.additionalData.ideSettings = newIDESettings;
55+
};
56+
migrationIDESettings();
57+
58+
const updateUserIDEInfo = async (selectedIde: string, useLatestVersion: boolean) => {
4359
const additionalData = user?.additionalData ?? {};
4460
const settings = additionalData.ideSettings ?? {};
45-
settings.useDesktopIde = useDesktopIde;
46-
settings.defaultIde = defaultIde;
47-
settings.defaultDesktopIde = desktopIde;
61+
settings.settingVersion = "2.0";
62+
settings.defaultIde = selectedIde;
4863
settings.useLatestVersion = useLatestVersion;
4964
additionalData.ideSettings = settings;
5065
getGitpodService()
5166
.server.trackEvent({
5267
event: "ide_configuration_changed",
53-
properties: {
54-
useDesktopIde,
55-
defaultIde,
56-
defaultDesktopIde: desktopIde,
57-
useLatestVersion,
58-
},
68+
properties: settings,
5969
})
6070
.then()
6171
.catch(console.error);
@@ -64,39 +74,27 @@ export default function Preferences() {
6474

6575
const [defaultIde, setDefaultIde] = useState<string>(user?.additionalData?.ideSettings?.defaultIde || "");
6676
const actuallySetDefaultIde = async (value: string) => {
67-
await updateUserIDEInfo(defaultDesktopIde, value, useLatestVersion);
77+
await updateUserIDEInfo(value, useLatestVersion);
6878
setDefaultIde(value);
6979
};
7080

71-
const [defaultDesktopIde, setDefaultDesktopIde] = useState<string>(
72-
(user?.additionalData?.ideSettings?.useDesktopIde && user?.additionalData?.ideSettings?.defaultDesktopIde) ||
73-
DesktopNoneId,
74-
);
75-
const actuallySetDefaultDesktopIde = async (value: string) => {
76-
await updateUserIDEInfo(value, defaultIde, useLatestVersion);
77-
setDefaultDesktopIde(value);
78-
};
79-
8081
const [useLatestVersion, setUseLatestVersion] = useState<boolean>(
8182
user?.additionalData?.ideSettings?.useLatestVersion ?? false,
8283
);
8384
const actuallySetUseLatestVersion = async (value: boolean) => {
84-
await updateUserIDEInfo(defaultDesktopIde, defaultIde, value);
85+
await updateUserIDEInfo(defaultIde, value);
8586
setUseLatestVersion(value);
8687
};
8788

8889
const [ideOptions, setIdeOptions] = useState<IDEOptions | undefined>(undefined);
8990
useEffect(() => {
9091
(async () => {
9192
const ideopts = await getGitpodService().server.getIDEOptions();
92-
ideopts.options[DesktopNoneId] = DesktopNone;
93+
9394
setIdeOptions(ideopts);
94-
if (!defaultIde) {
95+
if (!defaultIde || ideopts.options[defaultIde] == null) {
9596
setDefaultIde(ideopts.defaultIde);
9697
}
97-
if (!defaultDesktopIde) {
98-
setDefaultDesktopIde(ideopts.defaultDesktopIde);
99-
}
10098
})();
10199
}, []);
102100

@@ -114,8 +112,7 @@ export default function Preferences() {
114112
setTheme(theme);
115113
};
116114

117-
const browserIdeOptions = ideOptions && orderedIdeOptions(ideOptions, "browser");
118-
const desktopIdeOptions = ideOptions && orderedIdeOptions(ideOptions, "desktop");
115+
const allIdeOptions = ideOptions && orderedIdeOptions(ideOptions);
119116

120117
const [dotfileRepo, setDotfileRepo] = useState<string>(user?.additionalData?.dotfileRepo || "");
121118
const actuallySetDotfileRepo = async (value: string) => {
@@ -138,14 +135,14 @@ export default function Preferences() {
138135
>
139136
{ideOptions && (
140137
<>
141-
{browserIdeOptions && (
138+
{allIdeOptions && (
142139
<>
143-
<h3>Browser Editor</h3>
140+
<h3>Editor</h3>
144141
<p className="text-base text-gray-500 dark:text-gray-400">
145-
Choose the default editor for opening workspaces in the browser.
142+
Choose the editor for opening workspaces.
146143
</p>
147-
<div className="my-4 gap-4 flex flex-wrap">
148-
{browserIdeOptions.map(([id, option]) => {
144+
<div className="my-4 gap-4 flex flex-wrap max-w-2xl">
145+
{allIdeOptions.map(([id, option]) => {
149146
const selected = defaultIde === id;
150147
const onSelect = () => actuallySetDefaultIde(id);
151148
return renderIdeOption(option, selected, onSelect);
@@ -160,38 +157,6 @@ export default function Preferences() {
160157
</ul>
161158
</InfoBox>
162159
)}
163-
</>
164-
)}
165-
{desktopIdeOptions && (
166-
<>
167-
<h3 className="mt-12 flex">
168-
Desktop Editor
169-
<PillLabel type="warn" className="font-semibold py-0.5 px-2 self-center">
170-
Beta
171-
</PillLabel>
172-
</h3>
173-
<p className="text-base text-gray-500 dark:text-gray-400">
174-
Optionally, choose the default desktop editor for opening workspaces.
175-
</p>
176-
<div className="my-4 gap-4 flex flex-wrap max-w-2xl">
177-
{desktopIdeOptions.map(([id, option]) => {
178-
const selected = defaultDesktopIde === id;
179-
const onSelect = () => actuallySetDefaultDesktopIde(id);
180-
if (id === DesktopNoneId) {
181-
option.logo = isDark ? IDENoneDark : IDENone;
182-
}
183-
return renderIdeOption(option, selected, onSelect);
184-
})}
185-
</div>
186-
{ideOptions.options[defaultDesktopIde]?.notes && (
187-
<InfoBox className="my-5 max-w-2xl">
188-
<ul>
189-
{ideOptions.options[defaultDesktopIde].notes?.map((x, idx) => (
190-
<li className={idx > 0 ? "mt-2" : ""}>{x}</li>
191-
))}
192-
</ul>
193-
</InfoBox>
194-
)}
195160
<p className="text-left w-full text-gray-500">
196161
The <strong>JetBrains desktop IDEs</strong> are currently in beta.{" "}
197162
<a
@@ -215,8 +180,28 @@ export default function Preferences() {
215180
</>
216181
)}
217182
<CheckBox
218-
title="Latest Release"
219-
desc="Include the latest Early Access Program (EAP) version for each JetBrains IDE."
183+
title="Latest Release (Unstable)"
184+
desc={
185+
<span>
186+
Use the latest version for each editor.{" "}
187+
<a
188+
className="gp-link"
189+
target="_blank"
190+
href="https://code.visualstudio.com/blogs/2016/02/01/introducing_insiders_build"
191+
>
192+
Insiders
193+
</a>{" "}
194+
for VS Code,{" "}
195+
<a
196+
className="gp-link"
197+
target="_blank"
198+
href="https://www.jetbrains.com/resources/eap/"
199+
>
200+
EAP
201+
</a>{" "}
202+
for JetBrains IDEs.
203+
</span>
204+
}
220205
checked={useLatestVersion}
221206
onChange={(e) => actuallySetUseLatestVersion(e.target.checked)}
222207
/>
@@ -307,10 +292,10 @@ export default function Preferences() {
307292
);
308293
}
309294

310-
function orderedIdeOptions(ideOptions: IDEOptions, type: "browser" | "desktop") {
295+
function orderedIdeOptions(ideOptions: IDEOptions) {
311296
// TODO: Maybe convert orderKey to number before sort?
312297
return Object.entries(ideOptions.options)
313-
.filter(([_, x]) => x.type === type && !x.hidden)
298+
.filter(([_, x]) => !x.hidden)
314299
.sort((a, b) => {
315300
const keyA = a[1].orderKey || a[0];
316301
const keyB = b[1].orderKey || b[0];
@@ -319,23 +304,24 @@ function orderedIdeOptions(ideOptions: IDEOptions, type: "browser" | "desktop")
319304
}
320305

321306
function renderIdeOption(option: IDEOption, selected: boolean, onSelect: () => void): JSX.Element {
307+
const label = option.type === "desktop" ? "" : option.type;
322308
const card = (
323-
<SelectableCard className="w-36 h-40" title={option.title} selected={selected} onClick={onSelect}>
309+
<SelectableCardSolid className="w-36 h-40" title={option.title} selected={selected} onClick={onSelect}>
324310
<div className="flex justify-center mt-3">
325311
<img className="w-16 filter-grayscale self-center" src={option.logo} alt="logo" />
326312
</div>
327-
{option.label ? (
313+
{label ? (
328314
<div
329315
className={`font-semibold text-sm ${
330-
selected ? "text-green-500" : "text-gray-500 dark:text-gray-400"
316+
selected ? "text-gray-100 dark:text-gray-600" : "text-gray-600 dark:text-gray-500"
331317
} uppercase mt-2 px-3 py-1 self-center`}
332318
>
333-
{option.label}
319+
{label}
334320
</div>
335321
) : (
336322
<></>
337323
)}
338-
</SelectableCard>
324+
</SelectableCardSolid>
339325
);
340326

341327
if (option.tooltip) {

components/gitpod-protocol/src/protocol.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@ export interface EmailNotificationSettings {
115115
}
116116

117117
export type IDESettings = {
118+
settingVersion?: string;
118119
defaultIde?: string;
120+
// DEPRECATED: Use defaultIde after `settingVersion: 2.0`, no more specialify desktop or browser.
119121
useDesktopIde?: boolean;
122+
// DEPRECATED: Same with useDesktopIde.
120123
defaultDesktopIde?: string;
121124
useLatestVersion?: boolean;
122125
};

components/gitpod-protocol/src/workspace-instance.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ export interface WorkspaceInstanceRepoStatus {
204204
totalUnpushedCommits?: number;
205205
}
206206

207+
// ConfigurationIdeConfig ide config of WorkspaceInstanceConfiguration
208+
export interface ConfigurationIdeConfig {
209+
useLatest?: boolean;
210+
}
211+
207212
// WorkspaceInstanceConfiguration contains all per-instance configuration
208213
export interface WorkspaceInstanceConfiguration {
209214
// theiaVersion is the version of Theia this workspace instance uses
@@ -221,6 +226,8 @@ export interface WorkspaceInstanceConfiguration {
221226

222227
// supervisorImage is the ref of the supervisor image this instance uses.
223228
supervisorImage?: string;
229+
230+
ideConfig?: ConfigurationIdeConfig;
224231
}
225232

226233
/**

0 commit comments

Comments
 (0)