Skip to content

Commit 52a4331

Browse files
committed
[dashboard] query cache orgs
1 parent 4d04a75 commit 52a4331

35 files changed

+496
-688
lines changed

components/dashboard/src/App.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ import { useUserAndTeamsLoader } from "./hooks/use-user-and-teams-loader";
1212
import { useAnalyticsTracking } from "./hooks/use-analytics-tracking";
1313
import { AppLoading } from "./app/AppLoading";
1414
import { AppRoutes } from "./app/AppRoutes";
15-
import { useCurrentTeam } from "./teams/teams-context";
1615
import { useHistory } from "react-router";
16+
import { useCurrentOrg } from "./data/organizations/orgs-query";
1717

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

2020
// Top level Dashboard App component
2121
const App: FunctionComponent = () => {
22-
const { user, teams, isSetupRequired, loading } = useUserAndTeamsLoader();
23-
const currentOrg = useCurrentTeam();
22+
const { user, isSetupRequired, loading } = useUserAndTeamsLoader();
23+
const currentOrg = useCurrentOrg()?.data;
2424
const history = useHistory();
2525
useEffect(() => {
2626
return history.listen((location, action) => {
@@ -63,7 +63,7 @@ const App: FunctionComponent = () => {
6363
return (
6464
<Suspense fallback={<AppLoading />}>
6565
{/* Use org id as key to force re-render on org change */}
66-
<AppRoutes key={currentOrg?.id ?? "no-org"} user={user} teams={teams} />
66+
<AppRoutes key={currentOrg?.id ?? "no-org"} />
6767
</Suspense>
6868
);
6969
};

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

2826
function Item(props: { icon: string; iconSize?: string; text: string }) {
2927
const iconSize = props.iconSize || 28;
@@ -49,7 +47,6 @@ export function hasVisitedMarketingWebsiteBefore() {
4947

5048
export function Login() {
5149
const { setUser } = useContext(UserContext);
52-
const { setTeams } = useContext(TeamsContext);
5350

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

@@ -99,12 +96,8 @@ export function Login() {
9996

10097
const updateUser = async () => {
10198
await getGitpodService().reconnect();
102-
const [user, teams] = await Promise.all([
103-
getGitpodService().server.getLoggedInUser(),
104-
publicApiTeamsToProtocol((await teamsService.listTeams({})).teams),
105-
]);
99+
const [user] = await Promise.all([getGitpodService().server.getLoggedInUser()]);
106100
setUser(user);
107-
setTeams(teams);
108101
markLoggedIn();
109102
};
110103

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/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";
@@ -48,6 +48,7 @@ import { WebsocketClients } from "./WebsocketClients";
4848
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";
51+
import { useCurrentUser } from "../user-context";
5152

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

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

110112
// 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,27 +4,25 @@
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

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

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

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

6853
const setUsageAttributionTeam = async (team?: Team) => {
6954
if (!user) {
@@ -93,8 +78,8 @@ export function BillingAccountSelector(props: { onSelected?: () => void }) {
9378
{errorMessage}
9479
</Alert>
9580
)}
96-
{teamsAvailableForAttribution === undefined && <Spinner className="m-2 h-5 w-5 animate-spin" />}
97-
{teamsAvailableForAttribution && (
81+
{orgs.isLoading && <Spinner className="m-2 h-5 w-5 animate-spin" />}
82+
{orgs?.data && (
9883
<div>
9984
<h2 className="text-gray-500">
10085
Associate usage without a project to the billing account below.{" "}
@@ -126,24 +111,20 @@ export function BillingAccountSelector(props: { onSelected?: () => void }) {
126111
</div>
127112
</SelectableCardSolid>
128113
)}
129-
{teamsAvailableForAttribution.map((t) => (
114+
{teamsAvailableForAttribution.map((org) => (
130115
<SelectableCardSolid
131116
className="h-18"
132-
title={t.name}
133-
selected={isSelected("team", t.id)}
134-
onClick={() => setUsageAttributionTeam(t)}
117+
title={org.name}
118+
selected={isSelected("team", org.id)}
119+
onClick={() => setUsageAttributionTeam(org)}
135120
>
136121
<div className="flex-grow flex items-end px-1">
137122
<span
138123
className={`text-sm text-gray-400${
139-
isSelected("team", t.id) ? " dark:text-gray-600" : ""
124+
isSelected("team", org.id) ? " dark:text-gray-600" : ""
140125
}`}
141126
>
142-
{!!membersByTeam[t.id]
143-
? `${membersByTeam[t.id].length} member${
144-
membersByTeam[t.id].length === 1 ? "" : "s"
145-
}`
146-
: "..."}
127+
{`${org.members.length} member${org.members.length === 1 ? "" : "s"}`}
147128
</span>
148129
</div>
149130
</SelectableCardSolid>

components/dashboard/src/components/UsageLimitReachedModal.tsx

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

7-
import { Team } from "@gitpod/gitpod-protocol";
87
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
98
import { useEffect, useState } from "react";
9+
import { OrganizationInfo, useOrganizations } from "../data/organizations/orgs-query";
1010
import { settingsPathBilling } from "../user-settings/settings.routes";
11-
import { useTeams } from "../teams/teams-context";
1211
import Alert from "./Alert";
1312
import Modal from "./Modal";
1413

1514
export function UsageLimitReachedModal(p: { hints: any }) {
16-
const teams = useTeams();
17-
// const [attributionId, setAttributionId] = useState<AttributionId | undefined>();
18-
const [attributedTeam, setAttributedTeam] = useState<Team | undefined>();
15+
const orgs = useOrganizations();
16+
const [attributedTeam, setAttributedTeam] = useState<OrganizationInfo | undefined>();
1917

2018
useEffect(() => {
2119
const attributionId: AttributionId | undefined = p.hints && p.hints.attributionId;
2220
if (attributionId) {
2321
// setAttributionId(attributionId);
2422
if (attributionId.kind === "team") {
25-
const team = teams?.find((t) => t.id === attributionId.teamId);
23+
const team = orgs?.data?.find((t) => t.id === attributionId.teamId);
2624
setAttributedTeam(team);
2725
}
2826
}
29-
}, []);
27+
}, [orgs?.data, p.hints]);
3028

3129
const attributedTeamName = attributedTeam?.name;
3230
const billingLink = attributedTeam ? "/billing" : settingsPathBilling;

components/dashboard/src/contexts/FeatureFlagContext.tsx

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

77
import React, { createContext, useContext, useState, useEffect, useMemo } from "react";
8+
import { useCurrentOrg, useOrganizations } from "../data/organizations/orgs-query";
89
import { getExperimentsClient } from "../experiments/client";
910
import { ProjectContext } from "../projects/project-context";
10-
import { useCurrentTeam, useTeams } from "../teams/teams-context";
1111
import { UserContext } from "../user-context";
1212

1313
interface FeatureFlagConfig {
@@ -35,9 +35,9 @@ const FeatureFlagContext = createContext<FeatureFlagsType>(defaultFeatureFlags);
3535

3636
const FeatureFlagContextProvider: React.FC = ({ children }) => {
3737
const { user } = useContext(UserContext);
38-
const teams = useTeams();
38+
const orgs = useOrganizations()?.data;
3939
const { project } = useContext(ProjectContext);
40-
const team = useCurrentTeam();
40+
const currentOrg = useCurrentOrg();
4141
const [startWithOptions, setStartWithOptions] = useState<boolean>(false);
4242
const [showUsageView, setShowUsageView] = useState<boolean>(false);
4343
const [isUsageBasedBillingEnabled, setIsUsageBasedBillingEnabled] = useState<boolean>(false);
@@ -71,12 +71,12 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
7171
for (const [flagName, config] of Object.entries(featureFlags)) {
7272
const value = async () => {
7373
// First check if the flag is non-default for any of the orgs
74-
for (const team of teams || []) {
74+
for (const org of orgs || []) {
7575
const flagValue = await getExperimentsClient().getValueAsync(flagName, config.defaultValue, {
7676
user,
7777
projectId: project?.id,
78-
teamId: team.id,
79-
teamName: team?.name,
78+
teamId: org.id,
79+
teamName: org?.name,
8080
});
8181

8282
if (flagValue !== config.defaultValue) {
@@ -89,8 +89,8 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
8989
const valueForUser = await getExperimentsClient().getValueAsync(flagName, config.defaultValue, {
9090
user,
9191
projectId: project?.id,
92-
teamId: team?.id,
93-
teamName: team?.name,
92+
teamId: currentOrg?.data?.id,
93+
teamName: currentOrg?.data?.name,
9494
});
9595

9696
return valueForUser;
@@ -100,7 +100,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
100100
config.setter(val);
101101
}
102102
})();
103-
}, [user, teams, team, project]);
103+
}, [user, orgs, currentOrg, project]);
104104

105105
const flags = useMemo(() => {
106106
return {

components/dashboard/src/data/auth-providers/delete-org-auth-provider-mutation.ts

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

77
import { useMutation, useQueryClient } from "@tanstack/react-query";
88
import { getGitpodService } from "../../service/service";
9-
import { useCurrentTeam } from "../../teams/teams-context";
9+
import { useCurrentOrg } from "../organizations/orgs-query";
1010
import { getOrgAuthProvidersQueryKey, OrgAuthProvidersQueryResult } from "./org-auth-providers-query";
1111

1212
type DeleteAuthProviderArgs = {
1313
providerId: string;
1414
};
1515
export const useDeleteOrgAuthProviderMutation = () => {
1616
const queryClient = useQueryClient();
17-
const organization = useCurrentTeam();
17+
const organization = useCurrentOrg()?.data;
1818

1919
return useMutation({
2020
mutationFn: async ({ providerId }: DeleteAuthProviderArgs) => {

components/dashboard/src/data/auth-providers/org-auth-providers-query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import { AuthProviderEntry } from "@gitpod/gitpod-protocol";
88
import { useQuery, useQueryClient } from "@tanstack/react-query";
99
import { useCallback } from "react";
1010
import { getGitpodService } from "../../service/service";
11-
import { useCurrentTeam } from "../../teams/teams-context";
11+
import { useCurrentOrg } from "../organizations/orgs-query";
1212

1313
export type OrgAuthProvidersQueryResult = AuthProviderEntry[];
1414
export const useOrgAuthProvidersQuery = () => {
15-
const organization = useCurrentTeam();
15+
const organization = useCurrentOrg()?.data;
1616

1717
return useQuery<OrgAuthProvidersQueryResult>({
1818
queryKey: getOrgAuthProvidersQueryKey(organization?.id ?? ""),

components/dashboard/src/data/billing-mode/org-billing-mode-query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
88
import { useQuery } from "@tanstack/react-query";
99
import { getGitpodService } from "../../service/service";
10-
import { useCurrentTeam } from "../../teams/teams-context";
10+
import { useCurrentOrg } from "../organizations/orgs-query";
1111

1212
type OrgBillingModeQueryResult = BillingMode;
1313

1414
export const useOrgBillingMode = () => {
15-
const team = useCurrentTeam();
15+
const team = useCurrentOrg()?.data;
1616

1717
return useQuery<OrgBillingModeQueryResult>({
1818
queryKey: getOrgBillingModeQueryKey(team?.id ?? ""),

0 commit comments

Comments
 (0)