Skip to content

Commit

Permalink
Fix build failed
Browse files Browse the repository at this point in the history
  • Loading branch information
mustard-mh committed Oct 11, 2023
1 parent 6b62051 commit 52a39ca
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 89 deletions.
6 changes: 3 additions & 3 deletions components/dashboard/src/components/AuthorizeGit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import { FC, useCallback, useContext } from "react";
import { Link } from "react-router-dom";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useCurrentOrg } from "../data/organizations/orgs-query";
import { openAuthorizeWindow } from "../provider-utils";
import { getGitpodService } from "../service/service";
import { UserContext, useCurrentUser } from "../user-context";
import { Button } from "./Button";
import { Heading2, Heading3, Subheading } from "./typography/headings";
import classNames from "classnames";
import { iconForAuthProvider, simplifyProviderName } from "../provider-utils";
import { useOrgMembersInfoQuery } from "../data/organizations/org-members-info-query";

export function useNeedsGitAuthorization() {
const authProviders = useAuthProviders();
Expand All @@ -28,7 +28,7 @@ export function useNeedsGitAuthorization() {

export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
const { setUser } = useContext(UserContext);
const org = useCurrentOrg();
const orgMembersInfo = useOrgMembersInfoQuery();
const authProviders = useAuthProviders();
const updateUser = useCallback(() => {
getGitpodService().server.getLoggedInUser().then(setUser);
Expand Down Expand Up @@ -62,7 +62,7 @@ export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
{verifiedProviders.length === 0 ? (
<>
<Heading3 className="pb-2">No Git integrations</Heading3>
{!!org.data?.isOwner ? (
{!!orgMembersInfo.data?.isOwner ? (
<div className="px-6">
<Subheading>You need to configure at least one Git integration.</Subheading>
<Link to="/settings/git">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Button } from "./Button";
import { useCreateHoldPaymentIntentMutation } from "../data/billing/create-hold-payment-intent-mutation";
import { useToast } from "./toasts/Toasts";
import { ProgressBar } from "./ProgressBar";
import { useOrgMembersInfoQuery } from "../data/organizations/org-members-info-query";

const BASE_USAGE_LIMIT_FOR_STRIPE_USERS = 1000;

Expand All @@ -33,6 +34,7 @@ let didAlreadyCallSubscribe = false;

export default function UsageBasedBillingConfig({ hideSubheading = false }: Props) {
const currentOrg = useCurrentOrg().data;
const orgMembersInfo = useOrgMembersInfoQuery().data;
const attrId = currentOrg ? AttributionId.create(currentOrg) : undefined;
const attributionId = attrId && AttributionId.render(attrId);
const [showUpdateLimitModal, setShowUpdateLimitModal] = useState<boolean>(false);
Expand Down Expand Up @@ -154,8 +156,8 @@ export default function UsageBasedBillingConfig({ hideSubheading = false }: Prop
// Pick a good initial value for the Stripe usage limit (base_limit * team_size)
// FIXME: Should we ask the customer to confirm or edit this default limit?
let limit = BASE_USAGE_LIMIT_FOR_STRIPE_USERS;
if (attrId?.kind === "team" && currentOrg) {
limit = BASE_USAGE_LIMIT_FOR_STRIPE_USERS * currentOrg.members.length;
if (attrId?.kind === "team" && orgMembersInfo) {
limit = BASE_USAGE_LIMIT_FOR_STRIPE_USERS * orgMembersInfo.members.length;
}
const newLimit = await getGitpodService().server.subscribeToStripe(
attributionId,
Expand Down Expand Up @@ -190,7 +192,7 @@ export default function UsageBasedBillingConfig({ hideSubheading = false }: Prop
);
}
},
[attrId?.kind, attributionId, currentOrg, location.pathname, refreshSubscriptionDetails],
[attrId?.kind, attributionId, orgMembersInfo, location.pathname, refreshSubscriptionDetails],
);

const showSpinner = !attributionId || isLoadingStripeSubscription || isCreatingSubscription;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2023 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 { useQuery } from "@tanstack/react-query";
import { useCurrentOrg } from "./orgs-query";
import { teamsService } from "../../service/public-api";

export const useOrgInvitationQuery = () => {
const org = useCurrentOrg().data;

return useQuery<{ invitationId?: string }>({
queryKey: getOrgInvitationQueryKey(org?.id ?? ""),
staleTime: 1000 * 60 * 10, // 10 minute
queryFn: async () => {
if (!org) {
throw new Error("No org selected.");
}

const resp = await teamsService.getTeamInvitation({ teamId: org.id });
return { invitationId: resp.teamInvitation?.id };
},
enabled: !!org,
});
};

export const getOrgInvitationQueryKey = (orgId: string) => ["org-invitation", { orgId }];
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2023 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 { OrgMemberInfo } from "@gitpod/gitpod-protocol";
import { useQuery } from "@tanstack/react-query";
import { useCurrentOrg } from "./orgs-query";
import { publicApiTeamMembersToProtocol, teamsService } from "../../service/public-api";
import { useCurrentUser } from "../../user-context";
import { TeamRole } from "@gitpod/public-api/lib/gitpod/experimental/v1/teams_pb";

export interface OrgMembersInfo {
members: OrgMemberInfo[];
isOwner: boolean;
}

export const useOrgMembersInfoQuery = () => {
const user = useCurrentUser();
const org = useCurrentOrg().data;

return useQuery<OrgMembersInfo>({
queryKey: getOrgMembersInfoQueryKey(org?.id ?? "", user?.id ?? ""),
staleTime: 1000 * 60 * 5, // 5 minute
queryFn: async () => {
if (!org) {
throw new Error("No org selected.");
}
const resp = await teamsService.listTeamMembers({ teamId: org.id });
return {
members: publicApiTeamMembersToProtocol(resp.members),
isOwner:
resp.members.findIndex((member) => member.userId === user?.id && member.role === TeamRole.OWNER) >=
0,
};
},
enabled: !!org && !!user,
});
};

export const getOrgMembersInfoQueryKey = (orgId: string, userId: string) => ["org-members", { orgId, userId }];
32 changes: 12 additions & 20 deletions components/dashboard/src/data/organizations/orgs-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@
* See License.AGPL.txt in the project root for license information.
*/

import { Organization, OrgMemberInfo, User } from "@gitpod/gitpod-protocol";
import { Organization, User } from "@gitpod/gitpod-protocol";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import { useLocation } from "react-router";
import { publicApiTeamMembersToProtocol, publicApiTeamToProtocol, teamsService } from "../../service/public-api";
import { teamsService } from "../../service/public-api";
import { useCurrentUser } from "../../user-context";
import { noPersistence } from "../setup";
import { getOrgInvitationQueryKey } from "./org-invitation-query";
import { getOrgMembersInfoQueryKey } from "./org-members-info-query";

export interface OrganizationInfo extends Organization {
members: OrgMemberInfo[];
isOwner: boolean;
invitationId?: string;
}
export type OrganizationInfo = Pick<Organization, "id" | "name" | "slug">;

export function useOrganizationsInvalidator() {
const user = useCurrentUser();
const org = useCurrentOrg();
const queryClient = useQueryClient();
return useCallback(() => {
console.log("Invalidating orgs... " + JSON.stringify(getQueryKey(user)));
queryClient.invalidateQueries(getQueryKey(user));
}, [user, queryClient]);
if (org.data?.id && user?.id) {
queryClient.invalidateQueries(getOrgInvitationQueryKey(org.data.id));
queryClient.invalidateQueries(getOrgMembersInfoQueryKey(org.data.id, user.id));
}
}, [user, org.data, queryClient]);
}

export function useOrganizations() {
Expand All @@ -39,18 +42,7 @@ export function useOrganizations() {
}

const response = await teamsService.listTeams({});
const result: OrganizationInfo[] = [];
for (const org of response.teams) {
const members = publicApiTeamMembersToProtocol(org.members || []);
const isOwner = members.some((m) => m.role === "owner" && m.userId === user?.id);
result.push({
...publicApiTeamToProtocol(org),
members,
isOwner,
invitationId: org.teamInvitation?.id,
});
}
return result;
return response.teams;
},
{
enabled: !!user,
Expand Down
14 changes: 9 additions & 5 deletions components/dashboard/src/menu/OrganizationSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { useCurrentOrg, useOrganizations } from "../data/organizations/orgs-quer
import { useLocation } from "react-router";
import { User } from "@gitpod/gitpod-protocol";
import { useOrgBillingMode } from "../data/billing-mode/org-billing-mode-query";
import { useOrgMembersInfoQuery } from "../data/organizations/org-members-info-query";

export default function OrganizationSelector() {
const user = useCurrentUser();
const orgs = useOrganizations();
const currentOrg = useCurrentOrg();
const orgMembersInfo = useOrgMembersInfoQuery().data;
const { data: billingMode } = useOrgBillingMode();
const getOrgURL = useGetOrgURL();

Expand All @@ -39,9 +41,9 @@ export default function OrganizationSelector() {
<CurrentOrgEntry
title={currentOrg.data.name}
subtitle={
!!currentOrg.data.members
? `${currentOrg.data.members.length} member${
currentOrg.data.members.length === 1 ? "" : "s"
!!orgMembersInfo?.members
? `${orgMembersInfo.members.length} member${
orgMembersInfo.members.length === 1 ? "" : "s"
}`
: "..."
}
Expand Down Expand Up @@ -71,7 +73,7 @@ export default function OrganizationSelector() {
link: "/usage",
});
// Show billing if user is an owner of current org
if (currentOrg.data.isOwner) {
if (orgMembersInfo?.isOwner) {
if (billingMode?.mode === "usage-based") {
linkEntries.push({
title: "Billing",
Expand Down Expand Up @@ -107,7 +109,9 @@ export default function OrganizationSelector() {
id={org.id}
title={org.name}
subtitle={
!!org.members ? `${org.members.length} member${org.members.length === 1 ? "" : "s"}` : "..."
!!orgMembersInfo?.members
? `${orgMembersInfo.members.length} member${orgMembersInfo.members.length === 1 ? "" : "s"}`
: "..."
}
/>
),
Expand Down
26 changes: 15 additions & 11 deletions components/dashboard/src/teams/Members.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ import { SpinnerLoader } from "../components/Loader";
import { Delayed } from "../components/Delayed";
import { InputField } from "../components/forms/InputField";
import { InputWithCopy } from "../components/InputWithCopy";
import { useOrgMembersInfoQuery } from "../data/organizations/org-members-info-query";
import { useOrgInvitationQuery } from "../data/organizations/org-invitation-query";

export default function MembersPage() {
const user = useCurrentUser();
const org = useCurrentOrg();
const orgInvitation = useOrgInvitationQuery();
const orgMembersInfo = useOrgMembersInfoQuery();
const invalidateOrgs = useOrganizationsInvalidator();

const [showInviteModal, setShowInviteModal] = useState<boolean>(false);
Expand All @@ -38,14 +42,14 @@ export default function MembersPage() {
}
// orgs without an invitation id invite members through their own login page
const link = new URL(window.location.href);
if (!org.data.invitationId) {
if (!orgInvitation.data?.invitationId) {
link.pathname = "/login/" + org.data.slug;
} else {
link.pathname = "/orgs/join";
link.search = "?inviteId=" + org.data.invitationId;
link.search = "?inviteId=" + orgInvitation.data.invitationId;
}
return link.href;
}, [org.data]);
}, [org.data, orgInvitation.data]);

const resetInviteLink = async () => {
await teamsService.resetTeamInvitation({ teamId: org.data?.id });
Expand All @@ -66,14 +70,14 @@ export default function MembersPage() {
};

const isRemainingOwner = useMemo(() => {
const owners = org.data?.members.filter((m) => m.role === "owner");
const owners = orgMembersInfo.data?.members.filter((m) => m.role === "owner");
return owners?.length === 1 && owners[0].userId === user?.id;
}, [org.data?.members, user?.id]);
}, [orgMembersInfo.data?.members, user?.id]);

const isOwner = useMemo(() => {
const owners = org.data?.members.filter((m) => m.role === "owner");
const owners = orgMembersInfo.data?.members.filter((m) => m.role === "owner");
return !!owners?.some((o) => o.userId === user?.id);
}, [org.data?.members, user?.id]);
}, [orgMembersInfo.data?.members, user?.id]);

// Note: We would hardly get here, but just in case. We should show a loader instead of blank section.
if (org.isLoading) {
Expand All @@ -85,7 +89,7 @@ export default function MembersPage() {
}

const filteredMembers =
org.data?.members.filter((m) => {
orgMembersInfo.data?.members.filter((m) => {
if (!!roleFilter && m.role !== roleFilter) {
return false;
}
Expand Down Expand Up @@ -204,7 +208,7 @@ export default function MembersPage() {
</ItemField>
<ItemField className="flex items-center my-auto">
<span className="text-gray-400 capitalize">
{org.data?.isOwner ? (
{orgMembersInfo.data?.isOwner ? (
<DropDown
customClasses="w-32"
activeEntry={m.role}
Expand Down Expand Up @@ -239,7 +243,7 @@ export default function MembersPage() {
!isRemainingOwner && removeTeamMember(m.userId),
},
]
: org.data?.isOwner
: orgMembersInfo.data?.isOwner
? [
{
title: "Remove",
Expand Down Expand Up @@ -267,7 +271,7 @@ export default function MembersPage() {
</InputField>
</ModalBody>
<ModalFooter>
{!!org?.data?.invitationId && (
{!!orgInvitation?.data?.invitationId && (
<button className="secondary" onClick={() => resetInviteLink()}>
Reset Invite Link
</button>
Expand Down
Loading

0 comments on commit 52a39ca

Please sign in to comment.