Skip to content

Commit edca0d5

Browse files
authored
[dashboard] unique project links (#18618)
1 parent c402032 commit edca0d5

File tree

2 files changed

+35
-20
lines changed

2 files changed

+35
-20
lines changed

components/dashboard/src/projects/project-context.tsx

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import { Project } from "@gitpod/gitpod-protocol";
88
import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
99
import { useHistory, useLocation, useRouteMatch } from "react-router";
10-
import { validate as uuidValidate } from "uuid";
1110
import { useCurrentOrg, useOrganizations } from "../data/organizations/orgs-query";
1211
import { listAllProjects } from "../service/public-api";
1312
import { useCurrentUser } from "../user-context";
@@ -27,25 +26,41 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
2726
return <ProjectContext.Provider value={ctx}>{children}</ProjectContext.Provider>;
2827
};
2928

30-
export function useProjectSlugs(): { projectSlug?: string; prebuildId?: string } {
31-
const projectsRouteMatch = useRouteMatch<{ projectSlug?: string; prebuildId?: string }>(
32-
"/projects/:projectSlug?/:prebuildId?",
33-
);
29+
interface ProjectInfo {
30+
id: string;
31+
name?: string;
32+
}
33+
34+
export function useProjectInfo(): ProjectInfo | undefined {
35+
const projectsRouteMatch = useRouteMatch<{ projectSlug?: string }>("/projects/:projectSlug");
3436

3537
return useMemo(() => {
3638
const projectSlug = projectsRouteMatch?.params.projectSlug;
37-
const result: { projectSlug?: string; prebuildId?: string } = {};
38-
const reservedProjectSlugs = ["new"];
39-
if (!projectSlug || reservedProjectSlugs.includes(projectSlug)) {
40-
return result;
39+
if (!projectSlug) {
40+
return undefined;
4141
}
42-
result.projectSlug = projectSlug;
43-
const prebuildId = projectsRouteMatch?.params.prebuildId;
44-
if (prebuildId && uuidValidate(prebuildId)) {
45-
result.prebuildId = projectsRouteMatch?.params.prebuildId;
42+
const result = parseProjectSlug(projectSlug);
43+
if (!result) {
44+
return undefined;
4645
}
4746
return result;
48-
}, [projectsRouteMatch?.params.projectSlug, projectsRouteMatch?.params.prebuildId]);
47+
}, [projectsRouteMatch?.params.projectSlug]);
48+
}
49+
50+
const pattern: RegExp = /^((.+)-)?([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$/;
51+
function parseProjectSlug(slug: string): ProjectInfo | undefined {
52+
const match = slug.match(pattern);
53+
54+
if (match) {
55+
const name = match[2];
56+
const id = match[3];
57+
return {
58+
name,
59+
id,
60+
};
61+
} else {
62+
return undefined;
63+
}
4964
}
5065

5166
export function useCurrentProject(): { project: Project | undefined; loading: boolean } {
@@ -54,7 +69,7 @@ export function useCurrentProject(): { project: Project | undefined; loading: bo
5469
const user = useCurrentUser();
5570
const org = useCurrentOrg();
5671
const orgs = useOrganizations();
57-
const slugs = useProjectSlugs();
72+
const projectInfo = useProjectInfo();
5873
const location = useLocation();
5974
const history = useHistory();
6075

@@ -65,7 +80,7 @@ export function useCurrentProject(): { project: Project | undefined; loading: bo
6580
// without a user we are still consider this loading
6681
return;
6782
}
68-
if (!slugs.projectSlug) {
83+
if (!projectInfo) {
6984
setProject(undefined);
7085
setLoading(false);
7186
return;
@@ -77,15 +92,15 @@ export function useCurrentProject(): { project: Project | undefined; loading: bo
7792
let projects = await listAllProjects({ orgId: org.data.id });
7893

7994
// Find project matching with slug, otherwise with name
80-
const project = projects.find((p) => Project.slug(p) === slugs.projectSlug);
95+
const project = projects.find((p) => p.id === projectInfo.id);
8196
if (!project && orgs.data) {
8297
// check other orgs
8398
for (const t of orgs.data || []) {
8499
if (t.id === org.data?.id) {
85100
continue;
86101
}
87102
const projects = await listAllProjects({ orgId: t.id });
88-
const project = projects.find((p) => Project.slug(p) === slugs.projectSlug);
103+
const project = projects.find((p) => p.id === projectInfo.id);
89104
if (project) {
90105
// redirect to the other org
91106
history.push(location.pathname + "?org=" + t.id);
@@ -95,7 +110,7 @@ export function useCurrentProject(): { project: Project | undefined; loading: bo
95110
setProject(project);
96111
setLoading(false);
97112
})();
98-
}, [slugs.projectSlug, setProject, org.data, user, orgs.data, location, history]);
113+
}, [setProject, org.data, user, orgs.data, location, history, projectInfo]);
99114

100115
return { project, loading };
101116
}

components/gitpod-protocol/src/teams-projects-protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export namespace Project {
5252
};
5353

5454
export function slug(p: Project): string {
55-
return p.slug || p.name || p.id;
55+
return (p.slug || p.name) + "-" + p.id;
5656
}
5757

5858
export interface Overview {

0 commit comments

Comments
 (0)