Skip to content

Commit 39e2525

Browse files
committed
[dashboard] remember options per repo
1 parent d18ee26 commit 39e2525

File tree

2 files changed

+52
-77
lines changed

2 files changed

+52
-77
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,14 @@ export default function Preferences() {
8181
[toast, setUser, workspaceTimeout, billingMode],
8282
);
8383

84-
const clearAutostartWorkspaceOptions = useCallback(async () => {
84+
const clearCreateWorkspaceOptions = useCallback(async () => {
8585
if (!user) {
8686
return;
8787
}
8888
AdditionalUserData.set(user, { workspaceAutostartOptions: [] });
8989
setUser(user);
9090
await getGitpodService().server.updateLoggedInUser(user);
91-
toast("Your autostart options were cleared.");
91+
toast("Workspace options have been cleared.");
9292
}, [setUser, toast, user]);
9393

9494
return (
@@ -107,9 +107,9 @@ export default function Preferences() {
107107
</a>
108108
</Subheading>
109109
<SelectIDE location="preferences" />
110-
<Heading3 className="mt-12">Autostart Options</Heading3>
111-
<Subheading>Forget any saved autostart options for all repositories.</Subheading>
112-
<Button className="mt-4" type="secondary" onClick={clearAutostartWorkspaceOptions}>
110+
<Heading3 className="mt-12">Workspace Options</Heading3>
111+
<Subheading>Clear last used options for creating workspaces.</Subheading>
112+
<Button className="mt-4" type="secondary" onClick={clearCreateWorkspaceOptions}>
113113
Reset Options
114114
</Button>
115115

components/dashboard/src/workspaces/CreateWorkspacePage.tsx

Lines changed: 47 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,33 @@ import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
1010
import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred";
1111
import { FC, FunctionComponent, useCallback, useContext, useEffect, useMemo, useState } from "react";
1212
import { useHistory, useLocation } from "react-router";
13-
import { Link } from "react-router-dom";
13+
import Alert from "../components/Alert";
14+
import { AuthorizeGit, useNeedsGitAuthorization } from "../components/AuthorizeGit";
1415
import { Button } from "../components/Button";
16+
import { LinkButton } from "../components/LinkButton";
1517
import Modal from "../components/Modal";
1618
import RepositoryFinder from "../components/RepositoryFinder";
1719
import SelectIDEComponent from "../components/SelectIDEComponent";
1820
import SelectWorkspaceClassComponent from "../components/SelectWorkspaceClassComponent";
1921
import { UsageLimitReachedModal } from "../components/UsageLimitReachedModal";
20-
import { CheckboxInputField } from "../components/forms/CheckboxInputField";
22+
import { InputField } from "../components/forms/InputField";
2123
import { Heading1 } from "../components/typography/headings";
2224
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
2325
import { useCurrentOrg } from "../data/organizations/orgs-query";
2426
import { useListProjectsQuery } from "../data/projects/list-projects-query";
2527
import { useCreateWorkspaceMutation } from "../data/workspaces/create-workspace-mutation";
2628
import { useListWorkspacesQuery } from "../data/workspaces/list-workspaces-query";
2729
import { useWorkspaceContext } from "../data/workspaces/resolve-context-query";
30+
import { useDirtyState } from "../hooks/use-dirty-state";
2831
import { openAuthorizeWindow } from "../provider-utils";
2932
import { getGitpodService, gitpodHostUrl } from "../service/service";
3033
import { StartWorkspaceError } from "../start/StartPage";
3134
import { VerifyModal } from "../start/VerifyModal";
3235
import { StartWorkspaceOptions } from "../start/start-workspace-options";
3336
import { UserContext, useCurrentUser } from "../user-context";
3437
import { SelectAccountModal } from "../user-settings/SelectAccountModal";
35-
import { settingsPathPreferences } from "../user-settings/settings.routes";
36-
import { WorkspaceEntry } from "./WorkspaceEntry";
37-
import { AuthorizeGit, useNeedsGitAuthorization } from "../components/AuthorizeGit";
3838
import { settingsPathIntegrations } from "../user-settings/settings.routes";
39-
import { useDirtyState } from "../hooks/use-dirty-state";
40-
import { LinkButton } from "../components/LinkButton";
41-
import { InputField } from "../components/forms/InputField";
42-
import Alert from "../components/Alert";
39+
import { WorkspaceEntry } from "./WorkspaceEntry";
4340

4441
export function CreateWorkspacePage() {
4542
const { user, setUser } = useContext(UserContext);
@@ -68,12 +65,12 @@ export function CreateWorkspacePage() {
6865
const [contextURL, setContextURL] = useState<string | undefined>(
6966
StartWorkspaceOptions.parseContextUrl(location.hash),
7067
);
68+
const [optionsLoaded, setOptionsLoaded] = useState(false);
7169
// Currently this tracks if the user has selected a project from the dropdown
7270
// Need to make sure we initialize this to a project if the url hash value maps to a project's repo url
7371
// Will need to handle multiple projects w/ same repo url
7472
const [selectedProjectID, setSelectedProjectID] = useState<string | undefined>(undefined);
7573
const workspaceContext = useWorkspaceContext(contextURL);
76-
const [rememberOptions, setRememberOptions] = useState(false);
7774
const needsGitAuthorization = useNeedsGitAuthorization();
7875

7976
const storeAutoStartOptions = useCallback(async () => {
@@ -88,27 +85,25 @@ export function CreateWorkspacePage() {
8885
(e) => !(e.cloneURL === cloneURL && e.organizationId === currentOrg.id),
8986
);
9087

91-
// we only keep the last 20 options
88+
// we only keep the last 40 options
9289
workspaceAutoStartOptions = workspaceAutoStartOptions.slice(-40);
9390

94-
if (rememberOptions) {
95-
workspaceAutoStartOptions.push({
96-
cloneURL,
97-
organizationId: currentOrg.id,
98-
ideSettings: {
99-
defaultIde: selectedIde,
100-
useLatestVersion: useLatestIde,
101-
},
102-
workspaceClass: selectedWsClass,
103-
});
104-
}
91+
// remember options
92+
workspaceAutoStartOptions.push({
93+
cloneURL,
94+
organizationId: currentOrg.id,
95+
ideSettings: {
96+
defaultIde: selectedIde,
97+
useLatestVersion: useLatestIde,
98+
},
99+
workspaceClass: selectedWsClass,
100+
});
105101
AdditionalUserData.set(user, {
106102
workspaceAutostartOptions: workspaceAutoStartOptions,
107103
});
108104
setUser(user);
109105
await getGitpodService().server.updateLoggedInUser(user);
110-
console.log("Stored autostart options", workspaceAutoStartOptions);
111-
}, [currentOrg, rememberOptions, selectedIde, selectedWsClass, setUser, useLatestIde, user, workspaceContext.data]);
106+
}, [currentOrg, selectedIde, selectedWsClass, setUser, useLatestIde, user, workspaceContext.data]);
112107

113108
// see if we have a matching project based on context url and project's repo url
114109
const project = useMemo(() => {
@@ -273,23 +268,22 @@ export function CreateWorkspacePage() {
273268

274269
// listen on auto start changes
275270
useEffect(() => {
276-
if (!autostart) {
271+
if (!autostart || !optionsLoaded) {
277272
return;
278273
}
279274
createWorkspace();
280-
}, [autostart, createWorkspace]);
275+
}, [autostart, optionsLoaded, createWorkspace]);
281276

282277
// when workspaceContext is available, we look up if options are remembered
283278
useEffect(() => {
284279
const cloneURL = CommitContext.is(workspaceContext.data) && workspaceContext.data.repository.cloneUrl;
285-
if (!cloneURL || autostart) {
280+
if (!cloneURL) {
286281
return undefined;
287282
}
288283
const rememberedOptions = (user?.additionalData?.workspaceAutostartOptions || []).find(
289284
(e) => e.cloneURL === cloneURL && e.organizationId === currentOrg?.id,
290285
);
291286
if (rememberedOptions) {
292-
setRememberOptions(true);
293287
if (!selectedIdeIsDirty) {
294288
setSelectedIde(rememberedOptions.ideSettings?.defaultIde, false);
295289
setUseLatestIde(!!rememberedOptions.ideSettings?.useLatestVersion);
@@ -298,20 +292,20 @@ export function CreateWorkspacePage() {
298292
if (!selectedWsClassIsDirty) {
299293
setSelectedWsClass(rememberedOptions.workspaceClass, false);
300294
}
301-
if (autostart === undefined) {
302-
setAutostart(true);
303-
}
304295
} else {
305-
setRememberOptions(false);
306296
// reset the ide settings to the user's default IF they haven't changed it manually
307297
if (!selectedIdeIsDirty) {
308298
setSelectedIde(defaultIde, false);
309299
setUseLatestIde(defaultLatestIde);
310300
}
301+
if (!selectedWsClassIsDirty) {
302+
setSelectedWsClass(defaultWorkspaceClass, false);
303+
}
311304
}
305+
setOptionsLoaded(true);
312306
// we only update the remembered options when the workspaceContext changes
313307
// eslint-disable-next-line react-hooks/exhaustive-deps
314-
}, [workspaceContext.data]);
308+
}, [workspaceContext.data, setOptionsLoaded]);
315309

316310
// Need a wrapper here so we call createWorkspace w/o any arguments
317311
const onClickCreate = useCallback(() => createWorkspace(), [createWorkspace]);
@@ -326,9 +320,24 @@ export function CreateWorkspacePage() {
326320
}
327321
}, [selectedIdeIsDirty, setSelectedIde, workspaceContext.data]);
328322

323+
// on error we disable auto start and consider options loaded
324+
useEffect(() => {
325+
if (workspaceContext.error || createWorkspaceMutation.error) {
326+
setAutostart(false);
327+
setOptionsLoaded(true);
328+
}
329+
}, [workspaceContext.error, createWorkspaceMutation.error]);
330+
329331
// Derive if the continue button is disabled based on current state
330332
const continueButtonDisabled = useMemo(() => {
331-
if (workspaceContext.isLoading || !contextURL || contextURL.length === 0 || !!errorIde || !!errorWsClass) {
333+
if (
334+
autostart ||
335+
workspaceContext.isLoading ||
336+
!contextURL ||
337+
contextURL.length === 0 ||
338+
!!errorIde ||
339+
!!errorWsClass
340+
) {
332341
return true;
333342
}
334343
if (workspaceContext.error) {
@@ -341,7 +350,7 @@ export function CreateWorkspacePage() {
341350
}
342351

343352
return false;
344-
}, [contextURL, errorIde, errorWsClass, workspaceContext.error, workspaceContext.isLoading]);
353+
}, [autostart, contextURL, errorIde, errorWsClass, workspaceContext.error, workspaceContext.isLoading]);
345354

346355
if (SelectAccountPayload.is(selectAccountError)) {
347356
return (
@@ -408,7 +417,7 @@ export function CreateWorkspacePage() {
408417
selectedIdeOption={selectedIde}
409418
useLatest={useLatestIde}
410419
disabled={createWorkspaceMutation.isStarting}
411-
loading={workspaceContext.isLoading}
420+
loading={workspaceContext.isLoading || !optionsLoaded}
412421
/>
413422
</InputField>
414423

@@ -418,7 +427,7 @@ export function CreateWorkspacePage() {
418427
setError={setErrorWsClass}
419428
selectedWorkspaceClass={selectedWsClass}
420429
disabled={createWorkspaceMutation.isStarting}
421-
loading={workspaceContext.isLoading}
430+
loading={workspaceContext.isLoading || !optionsLoaded}
422431
/>
423432
</InputField>
424433
</div>
@@ -427,20 +436,13 @@ export function CreateWorkspacePage() {
427436
onClick={onClickCreate}
428437
autoFocus={true}
429438
size="block"
430-
loading={createWorkspaceMutation.isStarting}
439+
loading={createWorkspaceMutation.isStarting || autostart}
431440
disabled={continueButtonDisabled}
432441
>
433442
{createWorkspaceMutation.isStarting ? "Opening Workspace ..." : "Continue"}
434443
</Button>
435444
</div>
436445

437-
{workspaceContext.data && (
438-
<RememberOptions
439-
disabled={workspaceContext.isLoading || createWorkspaceMutation.isStarting}
440-
checked={rememberOptions}
441-
onChange={setRememberOptions}
442-
/>
443-
)}
444446
{existingWorkspaces.length > 0 && !createWorkspaceMutation.isStarting && (
445447
<div className="w-full flex flex-col justify-end px-6">
446448
<p className="mt-6 text-center text-base">Running workspaces on this revision</p>
@@ -462,33 +464,6 @@ export function CreateWorkspacePage() {
462464
);
463465
}
464466

465-
function RememberOptions(params: { disabled?: boolean; checked: boolean; onChange: (checked: boolean) => void }) {
466-
const { disabled, checked, onChange } = params;
467-
468-
return (
469-
<>
470-
<div className={"w-full flex justify-center mt-3 px-8 mx-2"}>
471-
<CheckboxInputField
472-
label="Autostart with these options for this repository."
473-
checked={checked}
474-
disabled={disabled}
475-
topMargin={false}
476-
onChange={onChange}
477-
/>
478-
</div>
479-
<div className={"w-full flex justify-center px-8 mx-2"}>
480-
<p className="text-gray-400 dark:text-gray-500 text-sm">
481-
Don't worry, you can reset this anytime in your{" "}
482-
<Link to={settingsPathPreferences} className="gp-link">
483-
preferences
484-
</Link>
485-
.
486-
</p>
487-
</div>
488-
</>
489-
);
490-
}
491-
492467
function tryAuthorize(host: string, scopes?: string[]): Promise<SelectAccountPayload | undefined> {
493468
const result = new Deferred<SelectAccountPayload | undefined>();
494469
openAuthorizeWindow({

0 commit comments

Comments
 (0)