From ead0ba023c313ffa6524d7bfcd2a51d41e74eea5 Mon Sep 17 00:00:00 2001 From: Sven Efftinge Date: Thu, 28 Sep 2023 14:49:07 +0200 Subject: [PATCH] [dashboard] remember options per repo (#18835) --- .../src/user-settings/Preferences.tsx | 10 +- .../src/workspaces/CreateWorkspacePage.tsx | 119 +++++++----------- 2 files changed, 52 insertions(+), 77 deletions(-) diff --git a/components/dashboard/src/user-settings/Preferences.tsx b/components/dashboard/src/user-settings/Preferences.tsx index c0347de8fe0184..70f534921fca7d 100644 --- a/components/dashboard/src/user-settings/Preferences.tsx +++ b/components/dashboard/src/user-settings/Preferences.tsx @@ -81,14 +81,14 @@ export default function Preferences() { [toast, setUser, workspaceTimeout, billingMode], ); - const clearAutostartWorkspaceOptions = useCallback(async () => { + const clearCreateWorkspaceOptions = useCallback(async () => { if (!user) { return; } AdditionalUserData.set(user, { workspaceAutostartOptions: [] }); setUser(user); await getGitpodService().server.updateLoggedInUser(user); - toast("Your autostart options were cleared."); + toast("Workspace options have been cleared."); }, [setUser, toast, user]); return ( @@ -107,9 +107,9 @@ export default function Preferences() { - Autostart Options - Forget any saved autostart options for all repositories. - diff --git a/components/dashboard/src/workspaces/CreateWorkspacePage.tsx b/components/dashboard/src/workspaces/CreateWorkspacePage.tsx index 04c2650c6247d9..98ecadb84ac662 100644 --- a/components/dashboard/src/workspaces/CreateWorkspacePage.tsx +++ b/components/dashboard/src/workspaces/CreateWorkspacePage.tsx @@ -10,14 +10,16 @@ import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred"; import { FC, FunctionComponent, useCallback, useContext, useEffect, useMemo, useState } from "react"; import { useHistory, useLocation } from "react-router"; -import { Link } from "react-router-dom"; +import Alert from "../components/Alert"; +import { AuthorizeGit, useNeedsGitAuthorization } from "../components/AuthorizeGit"; import { Button } from "../components/Button"; +import { LinkButton } from "../components/LinkButton"; import Modal from "../components/Modal"; import RepositoryFinder from "../components/RepositoryFinder"; import SelectIDEComponent from "../components/SelectIDEComponent"; import SelectWorkspaceClassComponent from "../components/SelectWorkspaceClassComponent"; import { UsageLimitReachedModal } from "../components/UsageLimitReachedModal"; -import { CheckboxInputField } from "../components/forms/CheckboxInputField"; +import { InputField } from "../components/forms/InputField"; import { Heading1 } from "../components/typography/headings"; import { useAuthProviders } from "../data/auth-providers/auth-provider-query"; import { useCurrentOrg } from "../data/organizations/orgs-query"; @@ -25,6 +27,7 @@ import { useListProjectsQuery } from "../data/projects/list-projects-query"; import { useCreateWorkspaceMutation } from "../data/workspaces/create-workspace-mutation"; import { useListWorkspacesQuery } from "../data/workspaces/list-workspaces-query"; import { useWorkspaceContext } from "../data/workspaces/resolve-context-query"; +import { useDirtyState } from "../hooks/use-dirty-state"; import { openAuthorizeWindow } from "../provider-utils"; import { getGitpodService, gitpodHostUrl } from "../service/service"; import { StartWorkspaceError } from "../start/StartPage"; @@ -32,14 +35,8 @@ import { VerifyModal } from "../start/VerifyModal"; import { StartWorkspaceOptions } from "../start/start-workspace-options"; import { UserContext, useCurrentUser } from "../user-context"; import { SelectAccountModal } from "../user-settings/SelectAccountModal"; -import { settingsPathPreferences } from "../user-settings/settings.routes"; -import { WorkspaceEntry } from "./WorkspaceEntry"; -import { AuthorizeGit, useNeedsGitAuthorization } from "../components/AuthorizeGit"; import { settingsPathIntegrations } from "../user-settings/settings.routes"; -import { useDirtyState } from "../hooks/use-dirty-state"; -import { LinkButton } from "../components/LinkButton"; -import { InputField } from "../components/forms/InputField"; -import Alert from "../components/Alert"; +import { WorkspaceEntry } from "./WorkspaceEntry"; export function CreateWorkspacePage() { const { user, setUser } = useContext(UserContext); @@ -68,12 +65,12 @@ export function CreateWorkspacePage() { const [contextURL, setContextURL] = useState( StartWorkspaceOptions.parseContextUrl(location.hash), ); + const [optionsLoaded, setOptionsLoaded] = useState(false); // Currently this tracks if the user has selected a project from the dropdown // Need to make sure we initialize this to a project if the url hash value maps to a project's repo url // Will need to handle multiple projects w/ same repo url const [selectedProjectID, setSelectedProjectID] = useState(undefined); const workspaceContext = useWorkspaceContext(contextURL); - const [rememberOptions, setRememberOptions] = useState(false); const needsGitAuthorization = useNeedsGitAuthorization(); const storeAutoStartOptions = useCallback(async () => { @@ -88,27 +85,25 @@ export function CreateWorkspacePage() { (e) => !(e.cloneURL === cloneURL && e.organizationId === currentOrg.id), ); - // we only keep the last 20 options + // we only keep the last 40 options workspaceAutoStartOptions = workspaceAutoStartOptions.slice(-40); - if (rememberOptions) { - workspaceAutoStartOptions.push({ - cloneURL, - organizationId: currentOrg.id, - ideSettings: { - defaultIde: selectedIde, - useLatestVersion: useLatestIde, - }, - workspaceClass: selectedWsClass, - }); - } + // remember options + workspaceAutoStartOptions.push({ + cloneURL, + organizationId: currentOrg.id, + ideSettings: { + defaultIde: selectedIde, + useLatestVersion: useLatestIde, + }, + workspaceClass: selectedWsClass, + }); AdditionalUserData.set(user, { workspaceAutostartOptions: workspaceAutoStartOptions, }); setUser(user); await getGitpodService().server.updateLoggedInUser(user); - console.log("Stored autostart options", workspaceAutoStartOptions); - }, [currentOrg, rememberOptions, selectedIde, selectedWsClass, setUser, useLatestIde, user, workspaceContext.data]); + }, [currentOrg, selectedIde, selectedWsClass, setUser, useLatestIde, user, workspaceContext.data]); // see if we have a matching project based on context url and project's repo url const project = useMemo(() => { @@ -273,23 +268,22 @@ export function CreateWorkspacePage() { // listen on auto start changes useEffect(() => { - if (!autostart) { + if (!autostart || !optionsLoaded) { return; } createWorkspace(); - }, [autostart, createWorkspace]); + }, [autostart, optionsLoaded, createWorkspace]); // when workspaceContext is available, we look up if options are remembered useEffect(() => { const cloneURL = CommitContext.is(workspaceContext.data) && workspaceContext.data.repository.cloneUrl; - if (!cloneURL || autostart) { + if (!cloneURL) { return undefined; } const rememberedOptions = (user?.additionalData?.workspaceAutostartOptions || []).find( (e) => e.cloneURL === cloneURL && e.organizationId === currentOrg?.id, ); if (rememberedOptions) { - setRememberOptions(true); if (!selectedIdeIsDirty) { setSelectedIde(rememberedOptions.ideSettings?.defaultIde, false); setUseLatestIde(!!rememberedOptions.ideSettings?.useLatestVersion); @@ -298,20 +292,20 @@ export function CreateWorkspacePage() { if (!selectedWsClassIsDirty) { setSelectedWsClass(rememberedOptions.workspaceClass, false); } - if (autostart === undefined) { - setAutostart(true); - } } else { - setRememberOptions(false); // reset the ide settings to the user's default IF they haven't changed it manually if (!selectedIdeIsDirty) { setSelectedIde(defaultIde, false); setUseLatestIde(defaultLatestIde); } + if (!selectedWsClassIsDirty) { + setSelectedWsClass(defaultWorkspaceClass, false); + } } + setOptionsLoaded(true); // we only update the remembered options when the workspaceContext changes // eslint-disable-next-line react-hooks/exhaustive-deps - }, [workspaceContext.data]); + }, [workspaceContext.data, setOptionsLoaded]); // Need a wrapper here so we call createWorkspace w/o any arguments const onClickCreate = useCallback(() => createWorkspace(), [createWorkspace]); @@ -326,9 +320,24 @@ export function CreateWorkspacePage() { } }, [selectedIdeIsDirty, setSelectedIde, workspaceContext.data]); + // on error we disable auto start and consider options loaded + useEffect(() => { + if (workspaceContext.error || createWorkspaceMutation.error) { + setAutostart(false); + setOptionsLoaded(true); + } + }, [workspaceContext.error, createWorkspaceMutation.error]); + // Derive if the continue button is disabled based on current state const continueButtonDisabled = useMemo(() => { - if (workspaceContext.isLoading || !contextURL || contextURL.length === 0 || !!errorIde || !!errorWsClass) { + if ( + autostart || + workspaceContext.isLoading || + !contextURL || + contextURL.length === 0 || + !!errorIde || + !!errorWsClass + ) { return true; } if (workspaceContext.error) { @@ -341,7 +350,7 @@ export function CreateWorkspacePage() { } return false; - }, [contextURL, errorIde, errorWsClass, workspaceContext.error, workspaceContext.isLoading]); + }, [autostart, contextURL, errorIde, errorWsClass, workspaceContext.error, workspaceContext.isLoading]); if (SelectAccountPayload.is(selectAccountError)) { return ( @@ -408,7 +417,7 @@ export function CreateWorkspacePage() { selectedIdeOption={selectedIde} useLatest={useLatestIde} disabled={createWorkspaceMutation.isStarting} - loading={workspaceContext.isLoading} + loading={workspaceContext.isLoading || !optionsLoaded} /> @@ -418,7 +427,7 @@ export function CreateWorkspacePage() { setError={setErrorWsClass} selectedWorkspaceClass={selectedWsClass} disabled={createWorkspaceMutation.isStarting} - loading={workspaceContext.isLoading} + loading={workspaceContext.isLoading || !optionsLoaded} /> @@ -427,20 +436,13 @@ export function CreateWorkspacePage() { onClick={onClickCreate} autoFocus={true} size="block" - loading={createWorkspaceMutation.isStarting} + loading={createWorkspaceMutation.isStarting || autostart} disabled={continueButtonDisabled} > {createWorkspaceMutation.isStarting ? "Opening Workspace ..." : "Continue"} - {workspaceContext.data && ( - - )} {existingWorkspaces.length > 0 && !createWorkspaceMutation.isStarting && (

Running workspaces on this revision

@@ -462,33 +464,6 @@ export function CreateWorkspacePage() { ); } -function RememberOptions(params: { disabled?: boolean; checked: boolean; onChange: (checked: boolean) => void }) { - const { disabled, checked, onChange } = params; - - return ( - <> -
- -
-
-

- Don't worry, you can reset this anytime in your{" "} - - preferences - - . -

-
- - ); -} - function tryAuthorize(host: string, scopes?: string[]): Promise { const result = new Deferred(); openAuthorizeWindow({