Skip to content

Commit 8496ae0

Browse files
authored
[dashboard] query cache orgs (#16685)
1 parent b48dc33 commit 8496ae0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+555
-735
lines changed

components/dashboard/src/App.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,22 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import React, { FunctionComponent, Suspense, useEffect } from "react";
87
import * as GitpodCookie from "@gitpod/gitpod-protocol/lib/util/gitpod-cookie";
9-
import { Login } from "./Login";
10-
import { isGitpodIo } from "./utils";
11-
import { useUserAndTeamsLoader } from "./hooks/use-user-and-teams-loader";
12-
import { useAnalyticsTracking } from "./hooks/use-analytics-tracking";
8+
import React, { FunctionComponent, Suspense } from "react";
139
import { AppLoading } from "./app/AppLoading";
1410
import { AppRoutes } from "./app/AppRoutes";
15-
import { useCurrentTeam } from "./teams/teams-context";
16-
import { useHistory } from "react-router";
11+
import { useCurrentOrg } from "./data/organizations/orgs-query";
12+
import { useAnalyticsTracking } from "./hooks/use-analytics-tracking";
13+
import { useUserAndTeamsLoader } from "./hooks/use-user-and-teams-loader";
14+
import { Login } from "./Login";
15+
import { isGitpodIo } from "./utils";
1716

1817
const Setup = React.lazy(() => import(/* webpackPrefetch: true */ "./Setup"));
1918

2019
// Top level Dashboard App component
2120
const App: FunctionComponent = () => {
22-
const { user, teams, isSetupRequired, loading } = useUserAndTeamsLoader();
23-
const currentOrg = useCurrentTeam();
24-
const history = useHistory();
25-
useEffect(() => {
26-
return history.listen((location, action) => {
27-
console.log(location, action);
28-
});
29-
}, [history]);
21+
const { user, isSetupRequired, loading } = useUserAndTeamsLoader();
22+
const currentOrg = useCurrentOrg().data;
3023

3124
// Setup analytics/tracking
3225
useAnalyticsTracking();
@@ -63,7 +56,7 @@ const App: FunctionComponent = () => {
6356
return (
6457
<Suspense fallback={<AppLoading />}>
6558
{/* Use org id as key to force re-render on org change */}
66-
<AppRoutes key={currentOrg?.id ?? "no-org"} user={user} teams={teams} />
59+
<AppRoutes key={currentOrg?.id ?? "no-org"} />
6760
</Suspense>
6861
);
6962
};

components/dashboard/src/AppNotifications.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function AppNotifications() {
1515
const [notifications, setNotifications] = useState<AppNotification[]>([]);
1616

1717
useEffect(() => {
18-
let localState = getLocalStorageObject(KEY_APP_NOTIFICATIONS);
18+
const localState = getLocalStorageObject(KEY_APP_NOTIFICATIONS);
1919
if (Array.isArray(localState)) {
2020
setNotifications(convertToAppNotification(localState));
2121
return;

components/dashboard/src/Login.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
88
import * as GitpodCookie from "@gitpod/gitpod-protocol/lib/util/gitpod-cookie";
99
import { useContext, useEffect, useMemo, useState } from "react";
1010
import { UserContext } from "./user-context";
11-
import { TeamsContext } from "./teams/teams-context";
1211
import { getGitpodService } from "./service/service";
1312
import { iconForAuthProvider, openAuthorizeWindow, simplifyProviderName, getSafeURLRedirect } from "./provider-utils";
1413
import gitpod from "./images/gitpod.svg";
@@ -23,7 +22,6 @@ import prebuild from "./images/welcome/prebuild.svg";
2322
import exclamation from "./images/exclamation.svg";
2423
import { getURLHash } from "./utils";
2524
import ErrorMessage from "./components/ErrorMessage";
26-
import { publicApiTeamsToProtocol, teamsService } from "./service/public-api";
2725
import { Heading1, Heading2, Subheading } from "./components/typography/headings";
2826

2927
function Item(props: { icon: string; iconSize?: string; text: string }) {
@@ -50,7 +48,6 @@ export function hasVisitedMarketingWebsiteBefore() {
5048

5149
export function Login() {
5250
const { setUser } = useContext(UserContext);
53-
const { setTeams } = useContext(TeamsContext);
5451

5552
const urlHash = useMemo(() => getURLHash(), []);
5653

@@ -100,12 +97,8 @@ export function Login() {
10097

10198
const updateUser = async () => {
10299
await getGitpodService().reconnect();
103-
const [user, teams] = await Promise.all([
104-
getGitpodService().server.getLoggedInUser(),
105-
publicApiTeamsToProtocol((await teamsService.listTeams({})).teams),
106-
]);
100+
const [user] = await Promise.all([getGitpodService().server.getLoggedInUser()]);
107101
setUser(user);
108-
setTeams(teams);
109102
markLoggedIn();
110103
};
111104

components/dashboard/src/SwitchToPAYG.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import { resetAllNotifications } from "./AppNotifications";
2121
import { Plans } from "@gitpod/gitpod-protocol/lib/plans";
2222
import ContextMenu, { ContextMenuEntry } from "./components/ContextMenu";
2323
import CaretDown from "./icons/CaretDown.svg";
24-
import { TeamsContext, useCurrentTeam } from "./teams/teams-context";
2524
import { Team } from "@gitpod/gitpod-protocol";
2625
import { OrgEntry } from "./menu/OrganizationSelector";
26+
import { useCurrentOrg, useOrganizations } from "./data/organizations/orgs-query";
2727

2828
/**
2929
* Keys of known page params
@@ -62,8 +62,8 @@ function SwitchToPAYG() {
6262
phase: "call-to-action",
6363
});
6464

65-
const currentOrg = useCurrentTeam();
66-
const { teams } = useContext(TeamsContext);
65+
const currentOrg = useCurrentOrg().data;
66+
const orgs = useOrganizations().data;
6767
const [errorMessage, setErrorMessage] = useState<string | undefined>();
6868
const [selectedOrganization, setSelectedOrganization] = useState<Team | undefined>(undefined);
6969
const [showBillingSetupModal, setShowBillingSetupModal] = useState<boolean>(false);
@@ -427,7 +427,7 @@ function SwitchToPAYG() {
427427

428428
const planName = pageState.old?.planName || "Legacy Plan";
429429
const planDescription = pageState.old?.planDetails || "";
430-
const selectorEntries = getOrganizationSelectorEntries(teams || [], setSelectedOrganization);
430+
const selectorEntries = getOrganizationSelectorEntries(orgs || [], setSelectedOrganization);
431431
return (
432432
<div className="flex flex-col max-h-screen max-w-3xl mx-auto items-center w-full mt-24">
433433
<h1>{`Update your ${titleModifier}`}</h1>

components/dashboard/src/Usage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
88
import UsageView from "./components/UsageView";
9-
import { useCurrentTeam } from "./teams/teams-context";
9+
import { useCurrentOrg } from "./data/organizations/orgs-query";
1010
import { useCurrentUser } from "./user-context";
1111

1212
function Usage() {
1313
const user = useCurrentUser();
14-
const org = useCurrentTeam();
14+
const org = useCurrentOrg().data;
1515

1616
if (!user) {
1717
return <></>;

components/dashboard/src/admin/BlockedRepositories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export function BlockedRepositoriesList(props: Props) {
6262
};
6363
useEffect(() => {
6464
search(); // Initial list
65+
// eslint-disable-next-line react-hooks/exhaustive-deps
6566
}, []);
6667

6768
const add = () => {

components/dashboard/src/admin/License.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default function License() {
3030
const data = await getGitpodService().server.adminGetLicense();
3131
setLicense(data);
3232
})();
33+
// eslint-disable-next-line react-hooks/exhaustive-deps
3334
}, []);
3435

3536
const featureList = license?.enabledFeatures;

components/dashboard/src/admin/ProjectsSearch.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,12 @@ export function ProjectsSearch() {
5757
if (currentProject) {
5858
if (currentProject.userId) {
5959
const owner = await getGitpodService().server.adminGetUser(currentProject.userId);
60-
if (owner) {
61-
setCurrentProjectOwner(owner?.name);
62-
}
60+
setCurrentProjectOwner(owner.name);
6361
}
6462
if (currentProject.teamId) {
6563
const owner = await getGitpodService().server.adminGetTeamById(currentProject.teamId);
6664
if (owner) {
67-
setCurrentProjectOwner(owner?.name);
65+
setCurrentProjectOwner(owner.name);
6866
}
6967
}
7068
}

components/dashboard/src/admin/TeamDetail.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ export default function TeamDetail(props: { team: Team }) {
158158
<svg xmlns="http://www.w3.org/2000/svg" fill="none" className="h-4 w-4" viewBox="0 0 16 16">
159159
<path
160160
fill="#A8A29E"
161-
fill-rule="evenodd"
161+
fillRule="evenodd"
162162
d="M13.366 8.234a.8.8 0 010 1.132l-4.8 4.8a.8.8 0 01-1.132 0l-4.8-4.8a.8.8 0 111.132-1.132L7.2 11.67V2.4a.8.8 0 111.6 0v9.269l3.434-3.435a.8.8 0 011.132 0z"
163-
clip-rule="evenodd"
163+
clipRule="evenodd"
164164
/>
165165
</svg>
166166
</ItemField>

components/dashboard/src/admin/TeamsSearch.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ export function TeamsSearch() {
111111
<svg xmlns="http://www.w3.org/2000/svg" fill="none" className="h-4 w-4" viewBox="0 0 16 16">
112112
<path
113113
fill="#A8A29E"
114-
fill-rule="evenodd"
114+
fillRule="evenodd"
115115
d="M13.366 8.234a.8.8 0 010 1.132l-4.8 4.8a.8.8 0 01-1.132 0l-4.8-4.8a.8.8 0 111.132-1.132L7.2 11.67V2.4a.8.8 0 111.6 0v9.269l3.434-3.435a.8.8 0 011.132 0z"
116-
clip-rule="evenodd"
116+
clipRule="evenodd"
117117
/>
118118
</svg>
119119
</div>

components/dashboard/src/admin/UserSearch.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ export default function UserSearch() {
100100
<svg xmlns="http://www.w3.org/2000/svg" fill="none" className="h-4 w-4" viewBox="0 0 16 16">
101101
<path
102102
fill="#A8A29E"
103-
fill-rule="evenodd"
103+
fillRule="evenodd"
104104
d="M13.366 8.234a.8.8 0 010 1.132l-4.8 4.8a.8.8 0 01-1.132 0l-4.8-4.8a.8.8 0 111.132-1.132L7.2 11.67V2.4a.8.8 0 111.6 0v9.269l3.434-3.435a.8.8 0 011.132 0z"
105-
clip-rule="evenodd"
105+
clipRule="evenodd"
106106
/>
107107
</svg>
108108
</div>

components/dashboard/src/app/AppRoutes.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { ContextURL, Team, User } from "@gitpod/gitpod-protocol";
8-
import React, { FunctionComponent, useContext, useState } from "react";
7+
import { ContextURL, User } from "@gitpod/gitpod-protocol";
8+
import React, { useContext, useState } from "react";
99
import { Redirect, Route, Switch, useLocation } from "react-router";
1010
import { AppNotifications } from "../AppNotifications";
1111
import Menu from "../menu/Menu";
@@ -49,6 +49,7 @@ import { StartWorkspaceOptions } from "../start/start-workspace-options";
4949
import { useFeatureFlags } from "../contexts/FeatureFlagContext";
5050
import { FORCE_ONBOARDING_PARAM, FORCE_ONBOARDING_PARAM_VALUE } from "../onboarding/UserOnboarding";
5151
import { Heading1, Subheading } from "../components/typography/headings";
52+
import { useCurrentUser } from "../user-context";
5253

5354
const Setup = React.lazy(() => import(/* webpackPrefetch: true */ "../Setup"));
5455
const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ "../workspaces/Workspaces"));
@@ -95,17 +96,18 @@ const Usage = React.lazy(() => import(/* webpackPrefetch: true */ "../Usage"));
9596
const UserOnboarding = React.lazy(() => import(/* webpackPrefetch: true */ "../onboarding/UserOnboarding"));
9697
const SwitchToPAYG = React.lazy(() => import(/* webpackPrefetch: true */ "../SwitchToPAYG"));
9798

98-
type AppRoutesProps = {
99-
user: User;
100-
teams?: Team[];
101-
};
102-
export const AppRoutes: FunctionComponent<AppRoutesProps> = ({ user, teams }) => {
99+
export const AppRoutes = () => {
103100
const hash = getURLHash();
101+
const user = useCurrentUser();
104102
const { startWorkspaceModalProps, setStartWorkspaceModalProps } = useContext(StartWorkspaceModalContext);
105-
const [isWhatsNewShown, setWhatsNewShown] = useState(shouldSeeWhatsNew(user));
103+
const [isWhatsNewShown, setWhatsNewShown] = useState(user && shouldSeeWhatsNew(user));
106104
const newCreateWsPage = useNewCreateWorkspacePage();
107105
const location = useLocation();
108106
const { newSignupFlow } = useFeatureFlags();
107+
108+
if (!user) {
109+
return <></>;
110+
}
109111
const search = new URLSearchParams(location.search);
110112

111113
// TODO: Add a Route for this instead of inspecting location manually

components/dashboard/src/app/OrgRequiredRoute.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
*/
66

77
import { Redirect, Route } from "react-router";
8-
import { useCurrentTeam } from "../teams/teams-context";
8+
import { useCurrentOrg } from "../data/organizations/orgs-query";
99

1010
// A wrapper for <Route> that redirects to "/" if there is not an organization currently selected
1111
// Having a check for an active org at the route level allows us to avoid any org-dependant api calls we might make
1212
// in page level components, and having to check for an org there
1313
export function OrgRequiredRoute({ component }: any) {
14-
const org = useCurrentTeam();
14+
const org = useCurrentOrg().data;
1515

1616
return <Route render={() => (!!org ? <Route component={component} /> : <Redirect to={"/"} />)} />;
1717
}

components/dashboard/src/components/BillingAccountSelector.tsx

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,26 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { useContext, useEffect, useState } from "react";
8-
import { Team, TeamMemberInfo } from "@gitpod/gitpod-protocol";
7+
import { Team } from "@gitpod/gitpod-protocol";
98
import { AttributionId, AttributionTarget } from "@gitpod/gitpod-protocol/lib/attribution";
10-
import { getGitpodService } from "../service/service";
11-
import { useTeams } from "../teams/teams-context";
12-
import { UserContext } from "../user-context";
9+
import { useContext, useEffect, useState } from "react";
1310
import SelectableCardSolid from "../components/SelectableCardSolid";
11+
import { OrganizationInfo, useOrganizations } from "../data/organizations/orgs-query";
1412
import { ReactComponent as Spinner } from "../icons/Spinner.svg";
13+
import { getGitpodService } from "../service/service";
14+
import { UserContext } from "../user-context";
1515
import Alert from "./Alert";
16-
import { publicApiTeamMembersToProtocol, teamsService } from "../service/public-api";
1716
import { Subheading } from "./typography/headings";
1817

1918
export function BillingAccountSelector(props: { onSelected?: () => void }) {
2019
const { user, setUser } = useContext(UserContext);
21-
const teams = useTeams();
22-
const [teamsAvailableForAttribution, setTeamsAvailableForAttribution] = useState<Team[] | undefined>();
23-
const [membersByTeam, setMembersByTeam] = useState<Record<string, TeamMemberInfo[]>>({});
20+
const orgs = useOrganizations();
21+
const [teamsAvailableForAttribution, setTeamsAvailableForAttribution] = useState<OrganizationInfo[]>([]);
2422
const [errorMessage, setErrorMessage] = useState<string | undefined>();
2523

2624
useEffect(() => {
27-
if (!teams) {
28-
setTeamsAvailableForAttribution(undefined);
25+
if (orgs.isLoading) {
26+
setTeamsAvailableForAttribution([]);
2927
return;
3028
}
3129

@@ -38,7 +36,7 @@ export function BillingAccountSelector(props: { onSelected?: () => void }) {
3836
if (attrId?.kind !== "team") {
3937
continue;
4038
}
41-
const team = teams.find((t) => t.id === attrId.teamId);
39+
const team = orgs.data?.find((t) => t.id === attrId.teamId);
4240
if (team) {
4341
teamsAvailableForAttribution.push(team);
4442
}
@@ -51,20 +49,7 @@ export function BillingAccountSelector(props: { onSelected?: () => void }) {
5149
console.error("Could not get list of available billing accounts.", error);
5250
setErrorMessage(`Could not get list of available billing accounts. ${error?.message || String(error)}`);
5351
});
54-
55-
const members: Record<string, TeamMemberInfo[]> = {};
56-
Promise.all(
57-
teams.map(async (team) => {
58-
try {
59-
members[team.id] = publicApiTeamMembersToProtocol(
60-
(await teamsService.getTeam({ teamId: team!.id })).team?.members || [],
61-
);
62-
} catch (error) {
63-
console.warn("Could not get members of org", team, error);
64-
}
65-
}),
66-
).then(() => setMembersByTeam(members));
67-
}, [teams]);
52+
}, [orgs]);
6853

6954
const setUsageAttributionTeam = async (team?: Team) => {
7055
if (!user) {
@@ -94,8 +79,8 @@ export function BillingAccountSelector(props: { onSelected?: () => void }) {
9479
{errorMessage}
9580
</Alert>
9681
)}
97-
{teamsAvailableForAttribution === undefined && <Spinner className="m-2 h-5 w-5 animate-spin" />}
98-
{teamsAvailableForAttribution && (
82+
{orgs.isLoading && <Spinner className="m-2 h-5 w-5 animate-spin" />}
83+
{orgs.data && (
9984
<div>
10085
<Subheading className="text-gray-500">
10186
Associate usage without a project to the billing account below.{" "}
@@ -127,24 +112,20 @@ export function BillingAccountSelector(props: { onSelected?: () => void }) {
127112
</div>
128113
</SelectableCardSolid>
129114
)}
130-
{teamsAvailableForAttribution.map((t) => (
115+
{teamsAvailableForAttribution.map((org) => (
131116
<SelectableCardSolid
132117
className="h-18"
133-
title={t.name}
134-
selected={isSelected("team", t.id)}
135-
onClick={() => setUsageAttributionTeam(t)}
118+
title={org.name}
119+
selected={isSelected("team", org.id)}
120+
onClick={() => setUsageAttributionTeam(org)}
136121
>
137122
<div className="flex-grow flex items-end px-1">
138123
<span
139124
className={`text-sm text-gray-400${
140-
isSelected("team", t.id) ? " dark:text-gray-600" : ""
125+
isSelected("team", org.id) ? " dark:text-gray-600" : ""
141126
}`}
142127
>
143-
{!!membersByTeam[t.id]
144-
? `${membersByTeam[t.id].length} member${
145-
membersByTeam[t.id].length === 1 ? "" : "s"
146-
}`
147-
: "..."}
128+
{`${org.members.length} member${org.members.length === 1 ? "" : "s"}`}
148129
</span>
149130
</div>
150131
</SelectableCardSolid>

0 commit comments

Comments
 (0)