diff --git a/components/dashboard/src/App.test.ts b/components/dashboard/src/App.test.ts new file mode 100644 index 00000000000000..6e0cc914ef389c --- /dev/null +++ b/components/dashboard/src/App.test.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2021 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 { getURLHash } from './App' + +test('urlHash', () => { + global.window = Object.create(window); + Object.defineProperty(window, 'location', { + value: { + hash: '#https://example.org/user/repo' + } + }); + + expect(getURLHash()).toBe('https://example.org/user/repo'); +}); \ No newline at end of file diff --git a/components/dashboard/src/App.tsx b/components/dashboard/src/App.tsx index 4dad6faecd89f3..461f1894a478d7 100644 --- a/components/dashboard/src/App.tsx +++ b/components/dashboard/src/App.tsx @@ -86,7 +86,7 @@ function isWebsiteSlug(pathName: string) { return slugs.some(slug => pathName.startsWith('/' + slug + '/') || pathName === ('/' + slug)); } -function getURLHash() { +export function getURLHash() { return window.location.hash.replace(/^[#/]+/, ''); } diff --git a/components/dashboard/src/Login.tsx b/components/dashboard/src/Login.tsx index f13eb85ab158f3..75a0ac0b7bbbda 100644 --- a/components/dashboard/src/Login.tsx +++ b/components/dashboard/src/Login.tsx @@ -11,6 +11,7 @@ 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 { Experiment } from './experiments'; import gitpod from './images/gitpod.svg'; import gitpodDark from './images/gitpod-dark.svg'; import gitpodIcon from './icons/gitpod.svg'; @@ -21,6 +22,7 @@ import customize from "./images/welcome/customize.svg"; import fresh from "./images/welcome/fresh.svg"; 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 }) { @@ -46,10 +48,29 @@ export function hasVisitedMarketingWebsiteBefore() { export function Login() { const { setUser } = useContext(UserContext); const { setTeams } = useContext(TeamsContext); - const showWelcome = !hasLoggedInBefore() && !hasVisitedMarketingWebsiteBefore(); - const [ authProviders, setAuthProviders ] = useState([]); - const [ errorMessage, setErrorMessage ] = useState(undefined); + const urlHash = getURLHash(); + let hostFromContext: string | undefined; + let repoPathname: string | undefined; + + try { + if (urlHash.length > 0) { + const url = new URL(urlHash); + hostFromContext = url.host; + repoPathname = url.pathname; + } + } catch (error) { + // Hash is not a valid URL + } + + const [authProviders, setAuthProviders] = useState([]); + const [errorMessage, setErrorMessage] = useState(undefined); + const [providerFromContext, setProviderFromContext] = useState(); + + const showWelcome = Experiment.has("login-from-context-6826") ? + (!hasLoggedInBefore() && !hasVisitedMarketingWebsiteBefore() && !urlHash.startsWith("https://")) + : (!hasLoggedInBefore() && !hasVisitedMarketingWebsiteBefore()) + ; useEffect(() => { (async () => { @@ -57,6 +78,16 @@ export function Login() { })(); }, []) + useEffect(() => { + if (!Experiment.has("login-from-context-6826")) { + return; + } + if (hostFromContext && authProviders) { + const providerFromContext = authProviders.find(provider => provider.host === hostFromContext); + setProviderFromContext(providerFromContext); + } + }, [authProviders]); + const authorizeSuccessful = async (payload?: string) => { updateUser().catch(console.error); @@ -70,7 +101,7 @@ export function Login() { const updateUser = async () => { await getGitpodService().reconnect(); - const [ user, teams ] = await Promise.all([ + const [user, teams] = await Promise.all([ getGitpodService().server.getLoggedInUser(), getGitpodService().server.getTeams(), ]); @@ -137,21 +168,37 @@ export function Login() {
- Gitpod's logo + Gitpod's logo + Gitpod dark theme logo
+
-

Log in{showWelcome ? '' : ' to Gitpod'}

-

ALWAYS READY-TO-CODE

+ {providerFromContext + ? <> +

Open a cloud-based development environment

+

for the repository {repoPathname?.slice(1)}

+ + : <> +

Log in{showWelcome ? '' : ' to Gitpod'}

+

ALWAYS READY-TO-CODE

+ }
+ +
- {authProviders.map(ap => { - return ( + {providerFromContext + ? + + : + authProviders.map(ap => - ); - })} + ) + }
{errorMessage && ( diff --git a/components/dashboard/src/experiments.ts b/components/dashboard/src/experiments.ts index 101c022e4a5fea..e40d62020dd566 100644 --- a/components/dashboard/src/experiments.ts +++ b/components/dashboard/src/experiments.ts @@ -28,6 +28,7 @@ const Experiments = { * Experiment "example" will be activate on login for 10% of all clients. */ // "example": 0.1, + "login-from-context-6826": 0.5, // https://github.com/gitpod-io/gitpod/issues/6826 }; const ExperimentsSet = new Set(Object.keys(Experiments)) as Set; export type Experiment = keyof (typeof Experiments); @@ -72,7 +73,7 @@ export namespace Experiment { if (arr === null) { return undefined; } - return new Set(arr) as Set; + return new Set(JSON.parse(arr)) as Set; } export function getAsArray(): Experiment[] {