Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laushinka/login 6826 #7046

Merged
merged 3 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions components/dashboard/src/App.test.ts
Original file line number Diff line number Diff line change
@@ -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');
});
2 changes: 1 addition & 1 deletion components/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(/^[#/]+/, '');
}

Expand Down
71 changes: 59 additions & 12 deletions components/dashboard/src/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 }) {
Expand All @@ -46,17 +48,46 @@ export function hasVisitedMarketingWebsiteBefore() {
export function Login() {
const { setUser } = useContext(UserContext);
const { setTeams } = useContext(TeamsContext);
const showWelcome = !hasLoggedInBefore() && !hasVisitedMarketingWebsiteBefore();

const [ authProviders, setAuthProviders ] = useState<AuthProviderInfo[]>([]);
const [ errorMessage, setErrorMessage ] = useState<string | undefined>(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<AuthProviderInfo[]>([]);
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [providerFromContext, setProviderFromContext] = useState<AuthProviderInfo>();

const showWelcome = Experiment.has("login-from-context-6826") ?
(!hasLoggedInBefore() && !hasVisitedMarketingWebsiteBefore() && !urlHash.startsWith("https://"))
: (!hasLoggedInBefore() && !hasVisitedMarketingWebsiteBefore())
;

useEffect(() => {
(async () => {
setAuthProviders(await getGitpodService().server.getAuthProviders());
})();
}, [])

useEffect(() => {
if (!Experiment.has("login-from-context-6826")) {
return;
}
if (hostFromContext && authProviders) {
laushinka marked this conversation as resolved.
Show resolved Hide resolved
const providerFromContext = authProviders.find(provider => provider.host === hostFromContext);
setProviderFromContext(providerFromContext);
}
}, [authProviders]);

const authorizeSuccessful = async (payload?: string) => {
updateUser().catch(console.error);

Expand All @@ -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(),
]);
Expand Down Expand Up @@ -137,21 +168,37 @@ export function Login() {
<div className="flex-grow h-100 flex flex-row items-center justify-center" >
<div className="rounded-xl px-10 py-10 mx-auto">
<div className="mx-auto pb-8">
<img src={gitpodIcon} className="h-16 mx-auto" alt="Gitpod's logo" />
<img src={providerFromContext ? gitpod : gitpodIcon} className="h-14 mx-auto block dark:hidden" alt="Gitpod's logo" />
<img src={providerFromContext ? gitpodDark : gitpodIcon} className="h-14 hidden mx-auto dark:block" alt="Gitpod dark theme logo" />
</div>

<div className="mx-auto text-center pb-8 space-y-2">
<h1 className="text-3xl">Log in{showWelcome ? '' : ' to Gitpod'}</h1>
<h2 className="uppercase text-sm text-gray-400">ALWAYS READY-TO-CODE</h2>
{providerFromContext
? <>
<h2 className="text-xl text-black dark:text-gray-50 font-semibold">Open a cloud-based development environment</h2>
<h2 className="text-xl">for the repository {repoPathname?.slice(1)}</h2>
</>
: <>
<h1 className="text-3xl">Log in{showWelcome ? '' : ' to Gitpod'}</h1>
<h2 className="uppercase text-sm text-gray-400">ALWAYS READY-TO-CODE</h2>
</>}
</div>


<div className="flex flex-col space-y-3 items-center">
{authProviders.map(ap => {
return (
{providerFromContext
?
<button key={"button" + providerFromContext.host} className="btn-login flex-none w-56 h-10 p-0 inline-flex" onClick={() => openLogin(providerFromContext.host)}>
{iconForAuthProvider(providerFromContext.authProviderType)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">Continue with {simplifyProviderName(providerFromContext.host)}</span>
</button>
:
authProviders.map(ap =>
<button key={"button" + ap.host} className="btn-login flex-none w-56 h-10 p-0 inline-flex" onClick={() => openLogin(ap.host)}>
{iconForAuthProvider(ap.authProviderType)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">Continue with {simplifyProviderName(ap.host)}</span>
</button>
);
})}
</button>)
}
</div>

{errorMessage && (
Expand Down
3 changes: 2 additions & 1 deletion components/dashboard/src/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Experiment>;
export type Experiment = keyof (typeof Experiments);
Expand Down Expand Up @@ -72,7 +73,7 @@ export namespace Experiment {
if (arr === null) {
return undefined;
}
return new Set(arr) as Set<Experiment>;
return new Set(JSON.parse(arr)) as Set<Experiment>;
}

export function getAsArray(): Experiment[] {
Expand Down