diff --git a/.gitpod.yml b/.gitpod.yml
index 307695d657c99d..8e98fa5f3968fb 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -14,15 +14,15 @@ ports:
onOpen: ignore
- port: 9229
onOpen: ignore
- # Go proxy
+# Go proxy
- port: 9999
onOpen: ignore
- port: 13001
onOpen: ignore
- # Werft
+# Werft
- port: 7777
onOpen: ignore
- # Dev Theia
+# Dev Theia
- port: 13444
tasks:
- name: Add Harvester kubeconfig
@@ -56,3 +56,5 @@ vscode:
- timonwong.shellcheck
- vscjava.vscode-java-pack
- fwcd.kotlin
+ - dbaeumer.vscode-eslint
+ - esbenp.prettier-vscode
diff --git a/components/dashboard/src/App.tsx b/components/dashboard/src/App.tsx
index 1cd3d457088c05..145601de845fc8 100644
--- a/components/dashboard/src/App.tsx
+++ b/components/dashboard/src/App.tsx
@@ -4,103 +4,123 @@
* See License-AGPL.txt in the project root for license information.
*/
-import React, { Suspense, useContext, useEffect, useState } from 'react';
-import Menu from './Menu';
+import React, { Suspense, useContext, useEffect, useState } from "react";
+import Menu from "./Menu";
import { Redirect, Route, Switch } from "react-router";
-import { Login } from './Login';
-import { UserContext } from './user-context';
-import { TeamsContext } from './teams/teams-context';
-import { ThemeContext } from './theme-context';
-import { AdminContext } from './admin-context';
-import { getGitpodService } from './service/service';
-import { shouldSeeWhatsNew, WhatsNew } from './whatsnew/WhatsNew';
-import gitpodIcon from './icons/gitpod.svg';
-import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
-import { useHistory } from 'react-router-dom';
-import { trackButtonOrAnchor, trackPathChange, trackLocation } from './Analytics';
-import { User } from '@gitpod/gitpod-protocol';
-import * as GitpodCookie from '@gitpod/gitpod-protocol/lib/util/gitpod-cookie';
-import { Experiment } from './experiments';
-import { workspacesPathMain } from './workspaces/workspaces.routes';
-import { settingsPathAccount, settingsPathIntegrations, settingsPathMain, settingsPathNotifications, settingsPathPlans, settingsPathPreferences, settingsPathTeams, settingsPathTeamsJoin, settingsPathTeamsNew, settingsPathVariables } from './settings/settings.routes';
-import { projectsPathInstallGitHubApp, projectsPathMain, projectsPathMainWithParams, projectsPathNew } from './projects/projects.routes';
-import { refreshSearchData } from './components/RepositoryFinder';
-import { StartWorkspaceModal } from './workspaces/StartWorkspaceModal';
-import { parseProps } from './start/StartWorkspace';
+import { Login } from "./Login";
+import { UserContext } from "./user-context";
+import { TeamsContext } from "./teams/teams-context";
+import { ThemeContext } from "./theme-context";
+import { AdminContext } from "./admin-context";
+import { getGitpodService } from "./service/service";
+import { shouldSeeWhatsNew, WhatsNew } from "./whatsnew/WhatsNew";
+import gitpodIcon from "./icons/gitpod.svg";
+import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
+import { useHistory } from "react-router-dom";
+import { trackButtonOrAnchor, trackPathChange, trackLocation } from "./Analytics";
+import { User } from "@gitpod/gitpod-protocol";
+import * as GitpodCookie from "@gitpod/gitpod-protocol/lib/util/gitpod-cookie";
+import { Experiment } from "./experiments";
+import { workspacesPathMain } from "./workspaces/workspaces.routes";
+import {
+ settingsPathAccount,
+ settingsPathIntegrations,
+ settingsPathMain,
+ settingsPathNotifications,
+ settingsPathPlans,
+ settingsPathPreferences,
+ settingsPathTeams,
+ settingsPathTeamsJoin,
+ settingsPathTeamsNew,
+ settingsPathVariables,
+} from "./settings/settings.routes";
+import {
+ projectsPathInstallGitHubApp,
+ projectsPathMain,
+ projectsPathMainWithParams,
+ projectsPathNew,
+} from "./projects/projects.routes";
+import { refreshSearchData } from "./components/RepositoryFinder";
+import { StartWorkspaceModal } from "./workspaces/StartWorkspaceModal";
+import { parseProps } from "./start/StartWorkspace";
-const Setup = React.lazy(() => import(/* webpackPrefetch: true */ './Setup'));
-const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ './workspaces/Workspaces'));
-const Account = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Account'));
-const Notifications = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Notifications'));
-const Plans = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Plans'));
-const Teams = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Teams'));
-const EnvironmentVariables = React.lazy(() => import(/* webpackPrefetch: true */ './settings/EnvironmentVariables'));
-const Integrations = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Integrations'));
-const Preferences = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Preferences'));
-const Open = React.lazy(() => import(/* webpackPrefetch: true */ './start/Open'));
-const StartWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './start/StartWorkspace'));
-const CreateWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './start/CreateWorkspace'));
-const NewTeam = React.lazy(() => import(/* webpackPrefetch: true */ './teams/NewTeam'));
-const JoinTeam = React.lazy(() => import(/* webpackPrefetch: true */ './teams/JoinTeam'));
-const Members = React.lazy(() => import(/* webpackPrefetch: true */ './teams/Members'));
-const TeamSettings = React.lazy(() => import(/* webpackPrefetch: true */ './teams/TeamSettings'));
-const NewProject = React.lazy(() => import(/* webpackPrefetch: true */ './projects/NewProject'));
-const ConfigureProject = React.lazy(() => import(/* webpackPrefetch: true */ './projects/ConfigureProject'));
-const Projects = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Projects'));
-const Project = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Project'));
-const ProjectSettings = React.lazy(() => import(/* webpackPrefetch: true */ './projects/ProjectSettings'));
-const ProjectVariables = React.lazy(() => import(/* webpackPrefetch: true */ './projects/ProjectVariables'));
-const Prebuilds = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Prebuilds'));
-const Prebuild = React.lazy(() => import(/* webpackPrefetch: true */ './projects/Prebuild'));
-const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ './projects/InstallGitHubApp'));
-const FromReferrer = React.lazy(() => import(/* webpackPrefetch: true */ './FromReferrer'));
-const UserSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/UserSearch'));
-const WorkspacesSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/WorkspacesSearch'));
-const AdminSettings = React.lazy(() => import(/* webpackPrefetch: true */ './admin/Settings'));
-const ProjectsSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/ProjectsSearch'));
-const TeamsSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/TeamsSearch'));
-const OAuthClientApproval = React.lazy(() => import(/* webpackPrefetch: true */ './OauthClientApproval'));
+const Setup = React.lazy(() => import(/* webpackPrefetch: true */ "./Setup"));
+const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ "./workspaces/Workspaces"));
+const Account = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/Account"));
+const Notifications = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/Notifications"));
+const Plans = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/Plans"));
+const Teams = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/Teams"));
+const EnvironmentVariables = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/EnvironmentVariables"));
+const Integrations = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/Integrations"));
+const Preferences = React.lazy(() => import(/* webpackPrefetch: true */ "./settings/Preferences"));
+const Open = React.lazy(() => import(/* webpackPrefetch: true */ "./start/Open"));
+const StartWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ "./start/StartWorkspace"));
+const CreateWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ "./start/CreateWorkspace"));
+const NewTeam = React.lazy(() => import(/* webpackPrefetch: true */ "./teams/NewTeam"));
+const JoinTeam = React.lazy(() => import(/* webpackPrefetch: true */ "./teams/JoinTeam"));
+const Members = React.lazy(() => import(/* webpackPrefetch: true */ "./teams/Members"));
+const TeamSettings = React.lazy(() => import(/* webpackPrefetch: true */ "./teams/TeamSettings"));
+const NewProject = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/NewProject"));
+const ConfigureProject = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/ConfigureProject"));
+const Projects = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/Projects"));
+const Project = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/Project"));
+const ProjectSettings = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/ProjectSettings"));
+const ProjectVariables = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/ProjectVariables"));
+const Prebuilds = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/Prebuilds"));
+const Prebuild = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/Prebuild"));
+const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ "./projects/InstallGitHubApp"));
+const FromReferrer = React.lazy(() => import(/* webpackPrefetch: true */ "./FromReferrer"));
+const UserSearch = React.lazy(() => import(/* webpackPrefetch: true */ "./admin/UserSearch"));
+const WorkspacesSearch = React.lazy(() => import(/* webpackPrefetch: true */ "./admin/WorkspacesSearch"));
+const AdminSettings = React.lazy(() => import(/* webpackPrefetch: true */ "./admin/Settings"));
+const ProjectsSearch = React.lazy(() => import(/* webpackPrefetch: true */ "./admin/ProjectsSearch"));
+const TeamsSearch = React.lazy(() => import(/* webpackPrefetch: true */ "./admin/TeamsSearch"));
+const OAuthClientApproval = React.lazy(() => import(/* webpackPrefetch: true */ "./OauthClientApproval"));
function Loading() {
- return <>
- >;
+ return <>>;
}
function isGitpodIo() {
- return window.location.hostname === 'gitpod.io' || window.location.hostname === 'gitpod-staging.com' || window.location.hostname.endsWith('gitpod-dev.com') || window.location.hostname.endsWith('gitpod-io-dev.com')
+ return (
+ window.location.hostname === "gitpod.io" ||
+ window.location.hostname === "gitpod-staging.com" ||
+ window.location.hostname.endsWith("gitpod-dev.com") ||
+ window.location.hostname.endsWith("gitpod-io-dev.com")
+ );
}
function isWebsiteSlug(pathName: string) {
const slugs = [
- 'about',
- 'blog',
- 'careers',
- 'changelog',
- 'chat',
- 'code-of-conduct',
- 'contact',
- 'docs',
- 'features',
- 'for',
- 'gitpod-vs-github-codespaces',
- 'imprint',
- 'media-kit',
- 'memes',
- 'pricing',
- 'privacy',
- 'security',
- 'screencasts',
- 'self-hosted',
- 'support',
- 'terms',
- 'values'
- ]
- return slugs.some(slug => pathName.startsWith('/' + slug + '/') || pathName === ('/' + slug));
+ "about",
+ "blog",
+ "careers",
+ "changelog",
+ "chat",
+ "code-of-conduct",
+ "contact",
+ "docs",
+ "features",
+ "for",
+ "gitpod-vs-github-codespaces",
+ "imprint",
+ "media-kit",
+ "memes",
+ "pricing",
+ "privacy",
+ "security",
+ "screencasts",
+ "self-hosted",
+ "support",
+ "terms",
+ "values",
+ ];
+ return slugs.some((slug) => pathName.startsWith("/" + slug + "/") || pathName === "/" + slug);
}
export function getURLHash() {
- return window.location.hash.replace(/^[#/]+/, '');
+ return window.location.hash.replace(/^[#/]+/, "");
}
function App() {
@@ -129,20 +149,19 @@ function App() {
// if a team was selected previously and we call the root URL (e.g. "gitpod.io"),
// let's continue with the team page
const hash = getURLHash();
- const isRoot = window.location.pathname === '/' && hash === '';
+ const isRoot = window.location.pathname === "/" && hash === "";
if (isRoot) {
try {
- const teamSlug = localStorage.getItem('team-selection');
- if (teams.some(t => t.slug === teamSlug)) {
+ const teamSlug = localStorage.getItem("team-selection");
+ if (teams.some((t) => t.slug === teamSlug)) {
history.push(`/t/${teamSlug}`);
}
- } catch {
- }
+ } catch {}
}
}
setTeams(teams);
- if (user?.rolesOrPermissions?.includes('admin')) {
+ if (user?.rolesOrPermissions?.includes("admin")) {
const adminSettings = await getGitpodService().server.adminGetSettings();
setAdminSettings(adminSettings);
}
@@ -159,32 +178,34 @@ function App() {
setLoading(false);
(window as any)._gp.path = window.location.pathname; //store current path to have access to previous when path changes
})();
- }, []);
+ }, [history, setAdminSettings, setTeams, setUser]);
useEffect(() => {
const updateTheme = () => {
- const isDark = localStorage.theme === 'dark' || (localStorage.theme !== 'light' && window.matchMedia("(prefers-color-scheme: dark)").matches);
+ const isDark =
+ localStorage.theme === "dark" ||
+ (localStorage.theme !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches);
setIsDark(isDark);
- }
+ };
updateTheme();
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
if (mediaQuery instanceof EventTarget) {
- mediaQuery.addEventListener('change', updateTheme);
+ mediaQuery.addEventListener("change", updateTheme);
} else {
// backward compatibility for Safari < 14
(mediaQuery as MediaQueryList).addListener(updateTheme);
}
- window.addEventListener('storage', updateTheme);
+ window.addEventListener("storage", updateTheme);
return function cleanup() {
if (mediaQuery instanceof EventTarget) {
- mediaQuery.removeEventListener('change', updateTheme);
+ mediaQuery.removeEventListener("change", updateTheme);
} else {
// backward compatibility for Safari < 14
(mediaQuery as MediaQueryList).removeListener(updateTheme);
}
- window.removeEventListener('storage', updateTheme);
- }
- }, []);
+ window.removeEventListener("storage", updateTheme);
+ };
+ }, [setIsDark]);
// listen and notify Segment of client-side path updates
useEffect(() => {
@@ -192,81 +213,97 @@ function App() {
// Choose which experiments to run for this session/user
Experiment.set(Experiment.seed(true));
}
- })
+ });
useEffect(() => {
return history.listen((location: any) => {
const path = window.location.pathname;
trackPathChange({
prev: (window as any)._gp.path,
- path: path
+ path: path,
});
(window as any)._gp.path = path;
- })
- }, [history])
+ });
+ }, [history]);
useEffect(() => {
const handleButtonOrAnchorTracking = (props: MouseEvent) => {
var curr = props.target as HTMLElement;
//check if current target or any ancestor up to document is button or anchor
while (!(curr instanceof Document)) {
- if (curr instanceof HTMLButtonElement || curr instanceof HTMLAnchorElement || (curr instanceof HTMLDivElement && curr.onclick)) {
+ if (
+ curr instanceof HTMLButtonElement ||
+ curr instanceof HTMLAnchorElement ||
+ (curr instanceof HTMLDivElement && curr.onclick)
+ ) {
trackButtonOrAnchor(curr);
break; //finding first ancestor is sufficient
}
curr = curr.parentNode as HTMLElement;
}
- }
+ };
window.addEventListener("click", handleButtonOrAnchorTracking, true);
return () => window.removeEventListener("click", handleButtonOrAnchorTracking, true);
}, []);
useEffect(() => {
if (user) {
- refreshSearchData('', user);
+ refreshSearchData("", user);
}
}, [user]);
// redirect to website for any website slugs
if (isGitpodIo() && isWebsiteSlug(window.location.pathname)) {
- window.location.host = 'www.gitpod.io';
+ window.location.host = "www.gitpod.io";
return
;
}
- if (isGitpodIo() && window.location.pathname === '/' && window.location.hash === '' && !loading && !user) {
+ if (isGitpodIo() && window.location.pathname === "/" && window.location.hash === "" && !loading && !user) {
if (!GitpodCookie.isPresent(document.cookie)) {
window.location.href = `https://www.gitpod.io`;
return
;
} else {
// explicitly render the Login page when the session is out-of-sync with the Gitpod cookie
- return ( );
+ return ;
}
}
if (loading) {
- return ( );
+ return ;
}
if (isSetupRequired) {
- return ( }>
-
- );
+ return (
+ }>
+
+
+ );
}
if (!user) {
- return ( );
+ return ;
}
- if (window.location.pathname.startsWith('/blocked')) {
- return ;
+ if (window.location.pathname.startsWith("/blocked")) {
+ return (
+
+ );
}
- const shouldWhatsNewShown = shouldSeeWhatsNew(user)
+ const shouldWhatsNewShown = shouldSeeWhatsNew(user);
if (shouldWhatsNewShown !== isWhatsNewShown) {
setWhatsNewShown(shouldWhatsNewShown);
}
- if (window.location.pathname.startsWith('/oauth-approval')) {
+ if (window.location.pathname.startsWith("/oauth-approval")) {
return (
}>
@@ -274,142 +311,162 @@ function App() {
);
}
- window.addEventListener("hashchange", () => {
- // Refresh on hash change if the path is '/' (new context URL)
- if (window.location.pathname === '/') {
- window.location.reload();
- }
- }, false);
+ window.addEventListener(
+ "hashchange",
+ () => {
+ // Refresh on hash change if the path is '/' (new context URL)
+ if (window.location.pathname === "/") {
+ window.location.reload();
+ }
+ },
+ false,
+ );
- let toRender: React.ReactElement =
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ let toRender: React.ReactElement = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Oh, no! Something went wrong!
-
{decodeURIComponent(getURLHash())}
-
-
-
-
- {
- const { resourceOrPrebuild } = props.match.params;
- if (resourceOrPrebuild === "settings") {
- return ;
- }
- if (resourceOrPrebuild === "configure") {
- return ;
- }
- if (resourceOrPrebuild === "variables") {
- return ;
- }
- if (resourceOrPrebuild === "prebuilds") {
- return ;
- }
- return resourceOrPrebuild ? : ;
- }} />
-
-
-
-
-
-
- {(teams || []).map(team =>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Oh, no! Something went wrong!
+
{decodeURIComponent(getURLHash())}
+
+
+
+
+ {
+ const { resourceOrPrebuild } = props.match.params;
+ if (resourceOrPrebuild === "settings") {
+ return ;
+ }
+ if (resourceOrPrebuild === "configure") {
+ return ;
+ }
+ if (resourceOrPrebuild === "variables") {
+ return ;
+ }
+ if (resourceOrPrebuild === "prebuilds") {
+ return ;
+ }
+ return resourceOrPrebuild ? : ;
+ }}
+ />
+
+
+
+
+
+
+ {(teams || []).map((team) => (
+
+
+
+
+ {
+ const { maybeProject, resourceOrPrebuild } = props.match.params;
+ if (maybeProject === "projects") {
+ return ;
+ }
+ if (maybeProject === "workspaces") {
+ return ;
+ }
+ if (maybeProject === "members") {
+ return ;
+ }
+ if (maybeProject === "settings") {
+ return ;
+ }
+ if (resourceOrPrebuild === "settings") {
+ return ;
+ }
+ if (resourceOrPrebuild === "configure") {
+ return ;
+ }
+ if (resourceOrPrebuild === "variables") {
+ return ;
+ }
+ if (resourceOrPrebuild === "prebuilds") {
+ return ;
+ }
+ return resourceOrPrebuild ? : ;
+ }}
+ />
- {
- const { maybeProject, resourceOrPrebuild } = props.match.params;
- if (maybeProject === "projects") {
- return ;
- }
- if (maybeProject === "workspaces") {
- return ;
- }
- if (maybeProject === "members") {
- return ;
- }
- if (maybeProject === "settings") {
- return ;
- }
- if (resourceOrPrebuild === "settings") {
- return ;
- }
- if (resourceOrPrebuild === "configure") {
- return ;
- }
- if (resourceOrPrebuild === "variables") {
- return ;
- }
- if (resourceOrPrebuild === "prebuilds") {
- return ;
- }
- return resourceOrPrebuild ? : ;
- }} />
- )}
- {
-
- return isGitpodIo() ?
- // delegate to our website to handle the request
- (window.location.host = 'www.gitpod.io') :
-
-
404
-
Page not found.
-
;
- }}>
-
-
-
-
- ;
+ ))}
+ {
+ return isGitpodIo() ? (
+ // delegate to our website to handle the request
+ (window.location.host = "www.gitpod.io")
+ ) : (
+
+
404
+
Page not found.
+
+ );
+ }}
+ >
+
+
+
+
+ );
const hash = getURLHash();
if (/^(https:\/\/)?github\.dev\//i.test(hash)) {
- window.location.hash = hash.replace(/^(https:\/\/)?github\.dev\//i, 'https://github.com/')
- return
- } else if (/^([^\/]+?=[^\/]*?|prebuild)\/(https:\/\/)?github\.dev\//i.test(hash)) {
- window.location.hash = hash.replace(/^([^\/]+?=[^\/]*?|prebuild)\/(https:\/\/)?github\.dev\//i, '$1/https://github.com/')
- return
+ window.location.hash = hash.replace(/^(https:\/\/)?github\.dev\//i, "https://github.com/");
+ return
;
+ } else if (/^([^/]+?=[^/]*?|prebuild)\/(https:\/\/)?github\.dev\//i.test(hash)) {
+ window.location.hash = hash.replace(
+ /^([^/]+?=[^/]*?|prebuild)\/(https:\/\/)?github\.dev\//i,
+ "$1/https://github.com/",
+ );
+ return
;
}
- const isCreation = window.location.pathname === '/' && hash !== '';
- const isWsStart = /\/start\/?/.test(window.location.pathname) && hash !== '';
+ const isCreation = window.location.pathname === "/" && hash !== "";
+ const isWsStart = /\/start\/?/.test(window.location.pathname) && hash !== "";
if (isWhatsNewShown) {
toRender = setWhatsNewShown(false)} />;
} else if (isCreation) {
@@ -417,18 +474,14 @@ function App() {
} else if (isWsStart) {
toRender = ;
} else if (/^(github|gitlab)\.com\/.+?/i.test(window.location.pathname)) {
- let url = new URL(window.location.href)
- url.hash = url.pathname
- url.pathname = '/'
- window.location.replace(url)
- return
+ let url = new URL(window.location.href);
+ url.hash = url.pathname;
+ url.pathname = "/";
+ window.location.replace(url);
+ return
;
}
- return (
- }>
- {toRender}
-
- );
+ return }>{toRender};
}
export default App;
diff --git a/components/dashboard/src/Login.tsx b/components/dashboard/src/Login.tsx
index 82b2fdd3ed07de..0cd81966e264fc 100644
--- a/components/dashboard/src/Login.tsx
+++ b/components/dashboard/src/Login.tsx
@@ -5,15 +5,15 @@
*/
import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
-import * as GitpodCookie from '@gitpod/gitpod-protocol/lib/util/gitpod-cookie';
+import * as GitpodCookie from "@gitpod/gitpod-protocol/lib/util/gitpod-cookie";
import { useContext, useEffect, useState } from "react";
import { UserContext } from "./user-context";
import { TeamsContext } from "./teams/teams-context";
import { getGitpodService } from "./service/service";
import { iconForAuthProvider, openAuthorizeWindow, simplifyProviderName, getSafeURLRedirect } from "./provider-utils";
-import gitpod from './images/gitpod.svg';
-import gitpodDark from './images/gitpod-dark.svg';
-import gitpodIcon from './icons/gitpod.svg';
+import gitpod from "./images/gitpod.svg";
+import gitpodDark from "./images/gitpod-dark.svg";
+import gitpodIcon from "./icons/gitpod.svg";
import automate from "./images/welcome/automate.svg";
import code from "./images/welcome/code.svg";
import collaborate from "./images/welcome/collaborate.svg";
@@ -23,13 +23,14 @@ import prebuild from "./images/welcome/prebuild.svg";
import exclamation from "./images/exclamation.svg";
import { getURLHash } from "./App";
-
-function Item(props: { icon: string, iconSize?: string, text: string }) {
+function Item(props: { icon: string; iconSize?: string; text: string }) {
const iconSize = props.iconSize || 28;
- return
-
-
{props.text}
-
;
+ return (
+
+
+
{props.text}
+
+ );
}
export function markLoggedIn() {
@@ -72,14 +73,14 @@ export function Login() {
(async () => {
setAuthProviders(await getGitpodService().server.getAuthProviders());
})();
- }, [])
+ }, []);
useEffect(() => {
if (hostFromContext && authProviders) {
- const providerFromContext = authProviders.find(provider => provider.host === hostFromContext);
+ const providerFromContext = authProviders.find((provider) => provider.host === hostFromContext);
setProviderFromContext(providerFromContext);
}
- }, [authProviders]);
+ }, [authProviders, hostFromContext]);
const authorizeSuccessful = async (payload?: string) => {
updateUser().catch(console.error);
@@ -90,7 +91,7 @@ export function Login() {
// ... and if it is, redirect to it
window.location.replace(safeReturnTo);
}
- }
+ };
const updateUser = async () => {
await getGitpodService().reconnect();
@@ -101,7 +102,7 @@ export function Login() {
setUser(user);
setTeams(teams);
markLoggedIn();
- }
+ };
const openLogin = async (host: string) => {
setErrorMessage(undefined);
@@ -118,102 +119,148 @@ export function Login() {
} else {
errorMessage = payload.description ? payload.description : `Error: ${payload.error}`;
if (payload.error === "email_taken") {
- errorMessage = `Email address already used in another account. Please log in with ${(payload as any).host}.`;
+ errorMessage = `Email address already used in another account. Please log in with ${
+ (payload as any).host
+ }.`;
}
}
setErrorMessage(errorMessage);
- }
+ },
});
} catch (error) {
- console.log(error)
+ console.log(error);
}
- }
+ };
- return (
- {showWelcome ?
-
-
-
-
-
-
-
-
Welcome to Gitpod
-
- Spin up fresh, automated dev environments for each task in the cloud, in seconds.
+ return (
+
+ {showWelcome ? (
+
+
+
+
+
+
+
+
+
Welcome to Gitpod
+
+ Spin up fresh, automated dev environments for each task in the cloud, in seconds.
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
: null}
-
-
-
-
-
-
-
-
-
-
- {providerFromContext
- ? <>
-
Open a cloud-based developer environment
- for the repository {repoPathname?.slice(1)}
- >
- : <>
- Log in{showWelcome ? '' : ' to Gitpod'}
- ALWAYS READY-TO-CODE
- >}
-
-
+ ) : null}
+
+
+
+
+
+
+
+
-
- {providerFromContext
- ?
- openLogin(providerFromContext.host)}>
- {iconForAuthProvider(providerFromContext.authProviderType)}
- Continue with {simplifyProviderName(providerFromContext.host)}
-
- :
- authProviders.map(ap =>
- openLogin(ap.host)}>
- {iconForAuthProvider(ap.authProviderType)}
- Continue with {simplifyProviderName(ap.host)}
- )
- }
-
+
+ {providerFromContext ? (
+ <>
+
+ Open a cloud-based developer environment
+
+ for the repository {repoPathname?.slice(1)}
+ >
+ ) : (
+ <>
+ Log in{showWelcome ? "" : " to Gitpod"}
+ ALWAYS READY-TO-CODE
+ >
+ )}
+
- {errorMessage && (
-
-
-
-
-
+
+ {providerFromContext ? (
+ openLogin(providerFromContext.host)}
+ >
+ {iconForAuthProvider(providerFromContext.authProviderType)}
+
+ Continue with {simplifyProviderName(providerFromContext.host)}
+
+
+ ) : (
+ authProviders.map((ap) => (
+ openLogin(ap.host)}
+ >
+ {iconForAuthProvider(ap.authProviderType)}
+
+ Continue with {simplifyProviderName(ap.host)}
+
+
+ ))
+ )}
- )}
+ {errorMessage && (
+
+
+
+
+
+
+ )}
+
+
+
-
-
-
-
);
+ );
}
diff --git a/components/dashboard/src/Menu.tsx b/components/dashboard/src/Menu.tsx
index 67c585a4f20d10..4453501e0256b3 100644
--- a/components/dashboard/src/Menu.tsx
+++ b/components/dashboard/src/Menu.tsx
@@ -9,14 +9,14 @@ import { useContext, useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { useLocation, useRouteMatch } from "react-router";
import { Location } from "history";
-import gitpodIcon from './icons/gitpod.svg';
+import gitpodIcon from "./icons/gitpod.svg";
import CaretDown from "./icons/CaretDown.svg";
import CaretUpDown from "./icons/CaretUpDown.svg";
import { getGitpodService, gitpodHostUrl } from "./service/service";
import { UserContext } from "./user-context";
import { TeamsContext, getCurrentTeam } from "./teams/teams-context";
-import settingsMenu from './settings/settings-menu';
-import { adminMenu } from './admin/admin-menu';
+import settingsMenu from "./settings/settings-menu";
+import { adminMenu } from "./admin/admin-menu";
import ContextMenu from "./components/ContextMenu";
import Separator from "./components/Separator";
import PillMenuItem from "./components/PillMenuItem";
@@ -26,9 +26,9 @@ import { getProjectSettingsMenu } from "./projects/ProjectSettings";
import { ProjectContext } from "./projects/project-context";
interface Entry {
- title: string,
- link: string,
- alternatives?: string[]
+ title: string;
+ link: string;
+ alternatives?: string[];
}
export default function Menu() {
@@ -38,7 +38,9 @@ export default function Menu() {
const team = getCurrentTeam(location, teams);
const { project, setProject } = useContext(ProjectContext);
- const match = useRouteMatch<{ segment1?: string, segment2?: string, segment3?: string }>("/(t/)?:segment1/:segment2?/:segment3?");
+ const match = useRouteMatch<{ segment1?: string; segment2?: string; segment3?: string }>(
+ "/(t/)?:segment1/:segment2?/:segment3?",
+ );
const projectSlug = (() => {
const resource = match?.params?.segment2;
if (resource && !["projects", "members", "users", "workspaces", "settings", "teams"].includes(resource)) {
@@ -47,59 +49,71 @@ export default function Menu() {
})();
const prebuildId = (() => {
const resource = projectSlug && match?.params?.segment3;
- if (resource !== "workspaces" && resource !== "prebuilds" && resource !== "settings" && resource !== "configure" && resource !== "variables") {
+ if (
+ resource !== "workspaces" &&
+ resource !== "prebuilds" &&
+ resource !== "settings" &&
+ resource !== "configure" &&
+ resource !== "variables"
+ ) {
return resource;
}
})();
function isSelected(entry: Entry, location: Location
) {
- const all = [entry.link, ...(entry.alternatives||[])].map(l => l.toLowerCase());
+ const all = [entry.link, ...(entry.alternatives || [])].map((l) => l.toLowerCase());
const path = location.pathname.toLowerCase();
- return all.some(n => n === path || n+'/' === path);
+ return all.some((n) => n === path || n + "/" === path);
}
- const userFullName = user?.fullName || user?.name || '...';
+ const userFullName = user?.fullName || user?.name || "...";
+ // TODO: This one smells like it could be some clever trickery,
+ // so opting for keeping it and disabling linting until
+ // Alex Tugarev chimes-in
+ // eslint-disable-next-line no-lone-blocks
{
// updating last team selection
try {
- localStorage.setItem('team-selection', team ? team.slug : "");
- } catch {
- }
+ localStorage.setItem("team-selection", team ? team.slug : "");
+ } catch {}
}
// Hide most of the top menu when in a full-page form.
- const isMinimalUI = ['/new', '/teams/new', '/open'].includes(location.pathname);
+ const isMinimalUI = ["/new", "/teams/new", "/open"].includes(location.pathname);
- const [ teamMembers, setTeamMembers ] = useState>({});
+ const [teamMembers, setTeamMembers] = useState>({});
useEffect(() => {
if (!teams) {
return;
}
(async () => {
const members: Record = {};
- await Promise.all(teams.map(async (team) => {
- try {
- members[team.id] = await getGitpodService().server.getTeamMembers(team.id);
- } catch (error) {
- console.error('Could not get members of team', team, error);
- }
- }));
+ await Promise.all(
+ teams.map(async (team) => {
+ try {
+ members[team.id] = await getGitpodService().server.getTeamMembers(team.id);
+ } catch (error) {
+ console.error("Could not get members of team", team, error);
+ }
+ }),
+ );
setTeamMembers(members);
})();
- }, [ teams ]);
+ }, [teams]);
useEffect(() => {
if (!teams || !projectSlug) {
return;
}
(async () => {
- const projects = (!!team
+ const projects = !!team
? await getGitpodService().server.getTeamProjects(team.id)
- : await getGitpodService().server.getUserProjects());
+ : await getGitpodService().server.getUserProjects();
// Find project matching with slug, otherwise with name
- const project = projectSlug && projects.find(p => p.slug ? p.slug === projectSlug : p.name === projectSlug);
+ const project =
+ projectSlug && projects.find((p) => (p.slug ? p.slug === projectSlug : p.name === projectSlug));
if (!project) {
return;
}
@@ -107,47 +121,47 @@ export default function Menu() {
})();
}, [projectSlug, setProject, team, teams]);
- const teamOrUserSlug = !!team ? '/t/' + team.slug : '/projects';
+ const teamOrUserSlug = !!team ? "/t/" + team.slug : "/projects";
const leftMenu: Entry[] = (() => {
// Project menu
if (projectSlug) {
return [
{
- title: 'Branches',
+ title: "Branches",
link: `${teamOrUserSlug}/${projectSlug}`,
},
{
- title: 'Prebuilds',
+ title: "Prebuilds",
link: `${teamOrUserSlug}/${projectSlug}/prebuilds`,
},
{
- title: 'Settings',
+ title: "Settings",
link: `${teamOrUserSlug}/${projectSlug}/settings`,
- alternatives: getProjectSettingsMenu({ slug: projectSlug } as Project, team).flatMap(e => e.link),
+ alternatives: getProjectSettingsMenu({ slug: projectSlug } as Project, team).flatMap((e) => e.link),
},
];
}
// Team menu
if (team) {
- const currentUserInTeam = (teamMembers[team.id] || []).find(m => m.userId === user?.id);
+ const currentUserInTeam = (teamMembers[team.id] || []).find((m) => m.userId === user?.id);
const teamSettingsList = [
{
- title: 'Projects',
+ title: "Projects",
link: `/t/${team.slug}/projects`,
- alternatives: ([] as string[])
+ alternatives: [] as string[],
},
{
- title: 'Members',
- link: `/t/${team.slug}/members`
- }
+ title: "Members",
+ link: `/t/${team.slug}/members`,
+ },
];
if (currentUserInTeam?.role === "owner") {
teamSettingsList.push({
- title: 'Settings',
+ title: "Settings",
link: `/t/${team.slug}/settings`,
- alternatives: getTeamSettingsMenu(team).flatMap(e => e.link),
- })
+ alternatives: getTeamSettingsMenu(team).flatMap((e) => e.link),
+ });
}
return teamSettingsList;
@@ -155,148 +169,226 @@ export default function Menu() {
// User menu
return [
{
- title: 'Workspaces',
- link: '/workspaces',
- alternatives: ['/']
+ title: "Workspaces",
+ link: "/workspaces",
+ alternatives: ["/"],
},
{
- title: 'Projects',
- link: '/projects'
+ title: "Projects",
+ link: "/projects",
},
{
- title: 'Settings',
- link: '/settings',
- alternatives: settingsMenu.flatMap(e => e.link)
- }
+ title: "Settings",
+ link: "/settings",
+ alternatives: settingsMenu.flatMap((e) => e.link),
+ },
];
})();
const rightMenu: Entry[] = [
- ...(user?.rolesOrPermissions?.includes('admin') ? [{
- title: 'Admin',
- link: '/admin',
- alternatives: adminMenu.flatMap(e => e.link)
- }] : []),
+ ...(user?.rolesOrPermissions?.includes("admin")
+ ? [
+ {
+ title: "Admin",
+ link: "/admin",
+ alternatives: adminMenu.flatMap((e) => e.link),
+ },
+ ]
+ : []),
{
- title: 'Docs',
- link: 'https://www.gitpod.io/docs/',
+ title: "Docs",
+ link: "https://www.gitpod.io/docs/",
},
{
- title: 'Help',
- link: 'https://www.gitpod.io/support',
- }
+ title: "Help",
+ link: "https://www.gitpod.io/support",
+ },
];
const renderTeamMenu = () => {
return (
- { projectSlug &&
-
- {team?.name || userFullName}
-
-
}
+ {projectSlug && (
+
+
+
+ {team?.name || userFullName}
+
+
+
+ )}
-
- {userFullName}
- Personal Account
-
,
- active: !team,
- separator: true,
- link: '/',
- },
- ...(teams || []).map(t => ({
- title: t.name,
- customContent:
- {t.name}
- {!!teamMembers[t.id]
- ? `${teamMembers[t.id].length} member${teamMembers[t.id].length === 1 ? '' : 's'}`
- : '...'
- }
-
,
- active: team && team.id === t.id,
- separator: true,
- link: `/t/${t.slug}`,
- })).sort((a, b) => a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1),
- {
- title: 'Create a new team',
- customContent:
,
- link: '/teams/new',
- }
- ]}>
+
+
+ {userFullName}
+
+ Personal Account
+
+ ),
+ active: !team,
+ separator: true,
+ link: "/",
+ },
+ ...(teams || [])
+ .map((t) => ({
+ title: t.name,
+ customContent: (
+
+
+ {t.name}
+
+
+ {!!teamMembers[t.id]
+ ? `${teamMembers[t.id].length} member${
+ teamMembers[t.id].length === 1 ? "" : "s"
+ }`
+ : "..."}
+
+
+ ),
+ active: team && team.id === t.id,
+ separator: true,
+ link: `/t/${t.slug}`,
+ }))
+ .sort((a, b) => (a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1)),
+ {
+ title: "Create a new team",
+ customContent: (
+
+ ),
+ link: "/teams/new",
+ },
+ ]}
+ >
- { !projectSlug &&
{team?.name || userFullName} }
-
+ {!projectSlug && (
+
+ {team?.name || userFullName}
+
+ )}
+
- { projectSlug && (
+ {projectSlug && (
- {project?.name}
+
+ {project?.name}
+
)}
- { prebuildId && (
+ {prebuildId && (
{prebuildId}
)}
- )
- }
+ );
+ };
- return <>
-
-
-
-
-
-
- {!isMinimalUI &&
- {renderTeamMenu()}
-
}
-
-