diff --git a/components/dashboard/src/app/AppRoutes.tsx b/components/dashboard/src/app/AppRoutes.tsx index db7eebb1d4cef8..f9f75a5dfa49ef 100644 --- a/components/dashboard/src/app/AppRoutes.tsx +++ b/components/dashboard/src/app/AppRoutes.tsx @@ -4,16 +4,14 @@ * See License.AGPL.txt in the project root for license information. */ -import { ContextURL, User } from "@gitpod/gitpod-protocol"; +import { User } from "@gitpod/gitpod-protocol"; import React, { useContext, useState } from "react"; import { Redirect, Route, Switch, useLocation } from "react-router"; import { AppNotifications } from "../AppNotifications"; import Menu from "../menu/Menu"; import OAuthClientApproval from "../OauthClientApproval"; import { projectsPathInstallGitHubApp, projectsPathNew } from "../projects/projects.routes"; -import { StartPage, StartPhase } from "../start/StartPage"; import { parseProps } from "../start/StartWorkspace"; -import SelectIDEModal from "../user-settings/SelectIDEModal"; import { settingsPathAccount, settingsPathBilling, @@ -137,16 +135,7 @@ export const AppRoutes = () => { // TODO: Try and encapsulate this in a route for "/" (check for hash in route component, render or redirect accordingly) const isCreation = location.pathname === "/" && hash !== ""; if (isCreation) { - // Prefix with `/#referrer` will specify an IDE for workspace - // After selection is saved, user will be updated, and this condition will be false - const showIDESelection = User.isOnboardingUser(user) && !hash.startsWith(ContextURL.REFERRER_PREFIX); - if (showIDESelection) { - return ( - - - - ); - } else if (new URLSearchParams(location.search).has("showOptions") || newCreateWsPage) { + if (new URLSearchParams(location.search).has("showOptions") || newCreateWsPage) { return ; } else { return ; diff --git a/components/dashboard/src/components/SelectIDEComponent.tsx b/components/dashboard/src/components/SelectIDEComponent.tsx index d5addce6ec3bde..9c953de377ebfe 100644 --- a/components/dashboard/src/components/SelectIDEComponent.tsx +++ b/components/dashboard/src/components/SelectIDEComponent.tsx @@ -32,12 +32,13 @@ export default function SelectIDEComponent(props: SelectIDEComponentProps) { for (const ide of options.filter((ide) => `${ide.label}${ide.title}${ide.notes}${ide.id}`.toLowerCase().includes(search.toLowerCase()), )) { - result.push({ - id: ide.id, - element: , - isSelectable: true, - }); - if (ide.latestImage) { + if (!props.useLatest) { + result.push({ + id: ide.id, + element: , + isSelectable: true, + }); + } else if (ide.latestImage) { result.push({ id: ide.id + "-latest", element: , @@ -47,7 +48,7 @@ export default function SelectIDEComponent(props: SelectIDEComponentProps) { } return result; }, - [ideOptions], + [ideOptions, props.useLatest], ); const internalOnSelectionChange = (id: string) => { const { ide, useLatest } = parseId(id); diff --git a/components/dashboard/src/user-settings/Preferences.tsx b/components/dashboard/src/user-settings/Preferences.tsx index 5035bc771ce4a9..72b57143368037 100644 --- a/components/dashboard/src/user-settings/Preferences.tsx +++ b/components/dashboard/src/user-settings/Preferences.tsx @@ -8,7 +8,6 @@ import { useCallback, useContext, useState } from "react"; import { getGitpodService } from "../service/service"; import { UserContext } from "../user-context"; import { trackEvent } from "../Analytics"; -import SelectIDE from "./SelectIDE"; import { PageWithSettingsSubMenu } from "./PageWithSettingsSubMenu"; import { ThemeSelector } from "../components/ThemeSelector"; import Alert from "../components/Alert"; @@ -16,6 +15,9 @@ import { Link } from "react-router-dom"; import { Heading2, Subheading } from "../components/typography/headings"; import { useUserMaySetTimeout } from "../data/current-user/may-set-timeout-query"; import { Button } from "../components/Button"; +import SelectIDE from "./SelectIDE"; + +export type IDEChangedTrackLocation = "workspace_list" | "workspace_start" | "preferences"; export default function Preferences() { const { user, setUser } = useContext(UserContext); @@ -74,7 +76,7 @@ export default function Preferences() { Learn more - + diff --git a/components/dashboard/src/user-settings/SelectIDE.tsx b/components/dashboard/src/user-settings/SelectIDE.tsx index 9dede2e9c420fe..6d6fdc541f28eb 100644 --- a/components/dashboard/src/user-settings/SelectIDE.tsx +++ b/components/dashboard/src/user-settings/SelectIDE.tsx @@ -1,19 +1,16 @@ /** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. + * Copyright (c) 2023 Gitpod GmbH. All rights reserved. * Licensed under the GNU Affero General Public License (AGPL). * See License.AGPL.txt in the project root for license information. */ -import { IDEOption, IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol"; import { useContext, useEffect, useState } from "react"; -import InfoBox from "../components/InfoBox"; -import SelectableCardSolid from "../components/SelectableCardSolid"; -import Tooltip from "../components/Tooltip"; -import { getGitpodService } from "../service/service"; import { UserContext } from "../user-context"; import CheckBox from "../components/CheckBox"; import { User } from "@gitpod/gitpod-protocol"; +import SelectIDEComponent from "../components/SelectIDEComponent"; import PillLabel from "../components/PillLabel"; +import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation"; export type IDEChangedTrackLocation = "workspace_list" | "workspace_start" | "preferences"; interface SelectIDEProps { @@ -21,33 +18,9 @@ interface SelectIDEProps { location: IDEChangedTrackLocation; } -export const updateUserIDEInfo = async ( - user: User, - selectedIde: string, - useLatestVersion: boolean, - location: IDEChangedTrackLocation, -) => { - const additionalData = user?.additionalData ?? {}; - const settings = additionalData.ideSettings ?? {}; - settings.settingVersion = "2.0"; - settings.defaultIde = selectedIde; - settings.useLatestVersion = useLatestVersion; - additionalData.ideSettings = settings; - getGitpodService() - .server.trackEvent({ - event: "ide_configuration_changed", - properties: { - ...settings, - location, - }, - }) - .then() - .catch(console.error); - return getGitpodService().server.updateLoggedInUser({ additionalData }); -}; - export default function SelectIDE(props: SelectIDEProps) { const { user, setUser } = useContext(UserContext); + const updateUser = useUpdateCurrentUserMutation(); // Only exec once when we access this component useEffect(() => { @@ -55,168 +28,96 @@ export default function SelectIDE(props: SelectIDEProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const actualUpdateUserIDEInfo = async (user: User, selectedIde: string, useLatestVersion: boolean) => { - const newUserData = await updateUserIDEInfo(user, selectedIde, useLatestVersion, props.location); + const [defaultIde, setDefaultIde] = useState(user?.additionalData?.ideSettings?.defaultIde || "code"); + const [useLatestVersion, setUseLatestVersion] = useState( + user?.additionalData?.ideSettings?.useLatestVersion ?? false, + ); + + const actualUpdateUserIDEInfo = async (selectedIde: string, useLatestVersion: boolean) => { + const additionalData = user?.additionalData ?? {}; + const settings = additionalData.ideSettings ?? {}; + settings.settingVersion = "2.0"; + settings.defaultIde = selectedIde; + settings.useLatestVersion = useLatestVersion; + additionalData.ideSettings = settings; + + const newUserData = await updateUser.mutateAsync({ additionalData }); props.updateUserContext && setUser({ ...newUserData }); }; - const [defaultIde, setDefaultIde] = useState(user?.additionalData?.ideSettings?.defaultIde || "code"); const actuallySetDefaultIde = async (value: string) => { - await actualUpdateUserIDEInfo(user!, value, useLatestVersion); + await actualUpdateUserIDEInfo(value, useLatestVersion); setDefaultIde(value); }; - const [useLatestVersion, setUseLatestVersion] = useState( - user?.additionalData?.ideSettings?.useLatestVersion ?? false, - ); const actuallySetUseLatestVersion = async (value: boolean) => { - await actualUpdateUserIDEInfo(user!, defaultIde, value); + await actualUpdateUserIDEInfo(defaultIde, value); setUseLatestVersion(value); }; - const [ideOptions, setIdeOptions] = useState(undefined); - useEffect(() => { - (async () => { - setIdeOptions(await getGitpodService().server.getIDEOptions()); - })(); - }, []); - - const allIdeOptions = ideOptions && orderedIdeOptions(ideOptions); + //todo(ft): find a better way to group IDEs by vendor + const shouldShowJetbrainsNotice = !["code", "code-desktop"].includes(defaultIde); // a really hacky way to get just JetBrains IDEs return ( <> - {ideOptions && ( - <> - {allIdeOptions && ( - <> -
- {allIdeOptions.map(([id, option]) => { - const selected = defaultIde === id; - const version = useLatestVersion ? option.latestImageVersion : option.imageVersion; - const onSelect = () => actuallySetDefaultIde(id); - return renderIdeOption(option, selected, version, onSelect); - })} -
- {ideOptions.options[defaultIde]?.notes && ( - -
    - {ideOptions.options[defaultIde].notes?.map((x, idx) => ( -
  • 0 ? "mt-2" : ""}>{x}
  • - ))} -
-
- )} - -

- JetBrains integration is currently in{" "} - - - Beta - - -  ·  - - Send feedback - -

- - )} - - Use the latest version for each editor.{" "} - - Insiders - {" "} - for VS Code,{" "} - - EAP - {" "} - for JetBrains IDEs. - - } - checked={useLatestVersion} - onChange={(e) => actuallySetUseLatestVersion(e.target.checked)} - /> - - )} - - ); -} - -function orderedIdeOptions(ideOptions: IDEOptions) { - // TODO: Maybe convert orderKey to number before sort? - return Object.entries(ideOptions.options) - .filter(([_, x]) => !x.hidden) - .sort((a, b) => { - const keyA = a[1].orderKey || a[0]; - const keyB = b[1].orderKey || b[0]; - return keyA.localeCompare(keyB); - }); -} - -function renderIdeOption( - option: IDEOption, - selected: boolean, - version: IDEOption["imageVersion"], - onSelect: () => void, -): JSX.Element { - const shouldShowOptionType = option.type !== "desktop" || option.title === "VS Code"; // Force show of "Desktop" in the list for VS Code Desktop - const card = ( - - {version ? ( - - {version} - - ) : ( - + { + await actuallySetDefaultIde(ide); }} - > - )} -
- logo + selectedIdeOption={defaultIde} + useLatest={useLatestVersion} + />
- {shouldShowOptionType ? ( - - {option.type} - - ) : ( - option.label && ( - - {option.label} + + {shouldShowJetbrainsNotice && ( +

+ JetBrains integration is currently in{" "} + + + Beta + - ) +  ·  + + Send feedback + +

)} -
- ); - if (option.tooltip) { - return {card}; - } - return card; + + Use the latest version for each editor.{" "} + + Insiders + {" "} + for VS Code,{" "} + + EAP + {" "} + for JetBrains IDEs. + + } + checked={useLatestVersion} + onChange={(e) => actuallySetUseLatestVersion(e.target.checked)} + /> + + ); } diff --git a/components/dashboard/src/user-settings/SelectIDEModal.tsx b/components/dashboard/src/user-settings/SelectIDEModal.tsx deleted file mode 100644 index 340d4837b01158..00000000000000 --- a/components/dashboard/src/user-settings/SelectIDEModal.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2022 Gitpod GmbH. All rights reserved. - * Licensed under the GNU Affero General Public License (AGPL). - * See License.AGPL.txt in the project root for license information. - */ - -import { useState, useContext } from "react"; -import { Link } from "react-router-dom"; -import { User } from "@gitpod/gitpod-protocol"; -import SelectIDE, { IDEChangedTrackLocation, updateUserIDEInfo } from "./SelectIDE"; -import Modal from "../components/Modal"; -import { UserContext } from "../user-context"; - -export interface SelectIDEModalProps { - location: IDEChangedTrackLocation; - onClose?: () => void; -} - -export default function SelectIDEModal(props: SelectIDEModalProps) { - const { user, setUser } = useContext(UserContext); - const [visible, setVisible] = useState(true); - - const actualUpdateUserIDEInfo = async (user: User, selectedIde: string, useLatestVersion: boolean) => { - const newUserData = await updateUserIDEInfo(user, selectedIde, useLatestVersion, props.location); - setUser({ ...newUserData }); - }; - - const handleContinue = async () => { - setVisible(false); - if (!user || User.hasPreferredIde(user)) { - props.onClose && props.onClose(); - return; - } - // TODO: We need to get defaultIde in ideOptions.. - const defaultIde = "code"; - await actualUpdateUserIDEInfo(user, defaultIde, false); - props.onClose && props.onClose(); - }; - - return ( - Continue} - > -

- Choose the editor for opening workspaces. You can always change later the editor in{" "} - - user preferences - - . -

- -
- ); -} diff --git a/components/dashboard/src/workspaces/Workspaces.tsx b/components/dashboard/src/workspaces/Workspaces.tsx index 83d46aaa19f418..2ed05825b030c9 100644 --- a/components/dashboard/src/workspaces/Workspaces.tsx +++ b/components/dashboard/src/workspaces/Workspaces.tsx @@ -8,29 +8,22 @@ import { FunctionComponent, useCallback, useMemo, useState } from "react"; import Header from "../components/Header"; import { WorkspaceEntry } from "./WorkspaceEntry"; import { ItemsList } from "../components/ItemsList"; -import { useCurrentUser } from "../user-context"; -import { User, WorkspaceInfo } from "@gitpod/gitpod-protocol"; -import SelectIDEModal from "../user-settings/SelectIDEModal"; +import { WorkspaceInfo } from "@gitpod/gitpod-protocol"; import Arrow from "../components/Arrow"; import ConfirmationModal from "../components/ConfirmationModal"; -import { ProfileState } from "../user-settings/ProfileInformation"; import { useListWorkspacesQuery } from "../data/workspaces/list-workspaces-query"; import { EmptyWorkspacesContent } from "./EmptyWorkspacesContent"; import { WorkspacesSearchBar } from "./WorkspacesSearchBar"; import { hoursBefore, isDateSmallerOrEqual } from "@gitpod/gitpod-protocol/lib/util/timeutil"; import { useDeleteInactiveWorkspacesMutation } from "../data/workspaces/delete-inactive-workspaces-mutation"; -import { useFeatureFlags } from "../contexts/FeatureFlagContext"; const WorkspacesPage: FunctionComponent = () => { - const user = useCurrentUser(); const [limit, setLimit] = useState(50); const [searchTerm, setSearchTerm] = useState(""); const [showInactive, setShowInactive] = useState(false); const [deleteModalVisible, setDeleteModalVisible] = useState(false); const { data, isLoading } = useListWorkspacesQuery({ limit }); - const isOnboardingUser = useMemo(() => user && User.isOnboardingUser(user), [user]); const deleteInactiveWorkspaces = useDeleteInactiveWorkspacesMutation(); - const { newSignupFlow } = useFeatureFlags(); // Sort workspaces into active/inactive groups const { activeWorkspaces, inactiveWorkspaces } = useMemo(() => { @@ -96,12 +89,6 @@ const WorkspacesPage: FunctionComponent = () => { /> )} - {/* TODO: can remove this once newSignupFlow flag is enabled */} - {isOnboardingUser && !newSignupFlow && } - - {/* TODO: can remove this once newSignupFlow flag is enabled */} - {!isOnboardingUser && !newSignupFlow && } - {!isLoading && (activeWorkspaces.length > 0 || inactiveWorkspaces.length > 0 || searchTerm ? ( <> diff --git a/components/dashboard/tailwind.config.js b/components/dashboard/tailwind.config.js index ec804e8b546e98..169575b206e67e 100644 --- a/components/dashboard/tailwind.config.js +++ b/components/dashboard/tailwind.config.js @@ -46,10 +46,6 @@ module.exports = { 112: "28rem", 128: "32rem", }, - maxWidth: { - // TODO(andreafalzetti): remove custom ide-modal class once we implement https://github.com/gitpod-io/gitpod/issues/13116 - 51.5: "51.5rem", - }, lineHeight: { 64: "64px", },