Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Fragment} from 'react';
import {useQuery} from '@tanstack/react-query';

import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import {openModal} from 'sentry/actionCreators/modal';
Expand All @@ -12,8 +13,9 @@ import type {
Integration,
} from 'sentry/types/integrations';
import type {Member} from 'sentry/types/organization';
import {apiOptions, selectJsonWithHeaders} from 'sentry/utils/api/apiOptions';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {fetchMutation, useApiQuery, useMutation} from 'sentry/utils/queryClient';
import {fetchMutation, useMutation} from 'sentry/utils/queryClient';
import {useOrganization} from 'sentry/utils/useOrganization';

import {IntegrationExternalMappingForm} from './integrationExternalMappingForm';
Expand All @@ -27,9 +29,6 @@ export function IntegrationExternalUserMappings(props: Props) {
const {integration} = props;
const organization = useOrganization();

const DATA_ENDPOINT = getApiUrl('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: organization.slug},
});
const BASE_FORM_ENDPOINT = getApiUrl(
'/organizations/$organizationIdOrSlug/external-users/',
{
Expand All @@ -38,23 +37,35 @@ export function IntegrationExternalUserMappings(props: Props) {
);
// We paginate on this query, since we're filtering by hasExternalTeams:true
const {
data: members = [],
data,
refetch: refetchMembers,
getResponseHeader,
isPending: isMembersPending,
isError: isMembersError,
} = useApiQuery<Array<Member & {externalUsers: ExternalUser[]}>>(
[DATA_ENDPOINT, {query: {query: 'hasExternalUsers:true', expand: 'externalUsers'}}],
{staleTime: 0}
);
const membersPageLinks = getResponseHeader?.('Link') ?? '';
} = useQuery({
...apiOptions.as<Array<Member & {externalUsers: ExternalUser[]}>>()(
'/organizations/$organizationIdOrSlug/members/',
{
path: {organizationIdOrSlug: organization.slug},
query: {query: 'hasExternalUsers:true', expand: 'externalUsers'},
staleTime: 0,
}
),
select: selectJsonWithHeaders,
});
const members = data?.json ?? [];
const membersPageLinks = data?.headers.Link ?? '';
// We use this query as defaultOptions to reduce identical API calls
const {
data: initialResults = [],
refetch: refetchInitialResults,
isPending: isInitialResultsPending,
isError: isInitialResultsError,
} = useApiQuery<Member[]>([DATA_ENDPOINT], {staleTime: 0});
} = useQuery(
apiOptions.as<Member[]>()('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: organization.slug},
staleTime: 0,
})
);

const fetchData = () => {
return Promise.all([refetchMembers(), refetchInitialResults()]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Fragment, useMemo, useState} from 'react';
import styled from '@emotion/styled';
import {useQuery, useQueryClient} from '@tanstack/react-query';

import {Button} from '@sentry/scraps/button';
import {Tooltip} from '@sentry/scraps/tooltip';
Expand All @@ -25,14 +26,10 @@ import {ConfigStore} from 'sentry/stores/configStore';
import type {OrganizationAuthProvider} from 'sentry/types/auth';
import type {Member} from 'sentry/types/organization';
import {trackAnalytics} from 'sentry/utils/analytics';
import {apiOptions, selectJsonWithHeaders} from 'sentry/utils/api/apiOptions';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {isDemoModeActive} from 'sentry/utils/demoMode';
import {
setApiQueryData,
useApiQuery,
useQueryClient,
type ApiQueryKey,
} from 'sentry/utils/queryClient';
import {setApiQueryData, useApiQuery, type ApiQueryKey} from 'sentry/utils/queryClient';
import {useApi} from 'sentry/utils/useApi';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
Expand All @@ -58,18 +55,19 @@ const InviteMembersButtonHook = HookOrDefault({
},
});

const getMembersQueryKey = ({
function membersApiOptions({
orgSlug,
query,
}: {
orgSlug: string;
query: Record<string, string>;
}): ApiQueryKey => [
getApiUrl('/organizations/$organizationIdOrSlug/members/', {
query?: Partial<Record<'query' | 'cursor', unknown>>;
}) {
return apiOptions.as<Member[]>()('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: orgSlug},
}),
{query},
];
query,
staleTime: 0,
});
}

const getInviteRequestsQueryKey = ({organization}: any): ApiQueryKey => [
getApiUrl('/organizations/$organizationIdOrSlug/invite-requests/', {
Expand Down Expand Up @@ -102,33 +100,32 @@ function OrganizationMembersList() {
],
{staleTime: 30000}
);
const membersQueryOptions = membersApiOptions({
orgSlug: organization.slug,
query: {
query: location.query.query,
cursor: location.query.cursor,
},
});
const {
data: members = [],
data: membersResponse,
isPending: isPendingMembers,
refetch: refetchMembers,
getResponseHeader,
} = useApiQuery<Member[]>(
getMembersQueryKey({
orgSlug: organization.slug,
query: {
query: location.query.query as string,
cursor: location.query.cursor as string,
},
}),
{staleTime: 0}
);
const {data: activeOwnerMembers = [], isPending: isPendingOwners} = useApiQuery<
Member[]
>(
getMembersQueryKey({
} = useQuery({
...membersQueryOptions,
select: selectJsonWithHeaders,
});
const members = useMemo(() => membersResponse?.json ?? [], [membersResponse?.json]);
const {data: activeOwnerMembers = [], isPending: isPendingOwners} = useQuery({
...membersApiOptions({
orgSlug: organization.slug,
// Ignore search queries since this isn't displayed, it's okay not to paginate.
// This is only used to determine if the current user is the only owner.
// We also filter out active invites, so only active users are included.
query: {query: 'role:owner isInvited:false'},
}),
{staleTime: 30000}
);
staleTime: 30_000,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should lint for this

});

const [invited, setInvited] = useState<Record<string, 'loading' | 'success' | null>>(
{}
Expand All @@ -140,16 +137,13 @@ function OrganizationMembersList() {
data: {},
});

setApiQueryData<Member[]>(
queryClient,
getMembersQueryKey({
orgSlug: organization.slug,
query: {
query: location.query.query as string,
cursor: location.query.cursor as string,
},
}),
currentMembers => currentMembers?.filter(member => member.id !== id)
queryClient.setQueryData(membersQueryOptions.queryKey, prevData =>
prevData
? {
...prevData,
json: prevData.json.filter(member => member.id !== id),
}
: prevData
);
};

Expand Down Expand Up @@ -305,7 +299,7 @@ function OrganizationMembersList() {

const searchQuery = (location.query.query as string) || '';

const membersPageLinks = getResponseHeader?.('Link');
const membersPageLinks = membersResponse?.headers.Link;

// hides other users in demo mode
const membersToShow = useMemo(
Expand Down
23 changes: 8 additions & 15 deletions static/app/views/settings/organizationTeams/teamMembers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ import {IconUser} from 'sentry/icons';
import {t} from 'sentry/locale';
import type {Member, Organization, Team, TeamMember} from 'sentry/types/organization';
import {apiOptions, selectJsonWithHeaders} from 'sentry/utils/api/apiOptions';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {useApiQuery, useMutation, useQueryClient} from 'sentry/utils/queryClient';
import {useMutation, useQueryClient} from 'sentry/utils/queryClient';
import {useApi} from 'sentry/utils/useApi';
import {useDebouncedValue} from 'sentry/utils/useDebouncedValue';
import {useLocation} from 'sentry/utils/useLocation';
Expand Down Expand Up @@ -85,20 +84,14 @@ function AddMemberDropdown({
}) {
const [memberQuery, setMemberQuery] = useState('');
const debouncedMemberQuery = useDebouncedValue(memberQuery, 50);
const {data: orgMembers = [], isFetching: isOrgMembersFetching} = useApiQuery<Member[]>(
[
getApiUrl('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: organization.slug},
}),
{
query: debouncedMemberQuery ? {query: debouncedMemberQuery} : undefined,
},
],
{
const {data: orgMembers = [], isFetching: isOrgMembersFetching} = useQuery({
...apiOptions.as<Member[]>()('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: organization.slug},
query: debouncedMemberQuery ? {query: debouncedMemberQuery} : undefined,
staleTime: 30_000,
placeholderData: keepPreviousData,
}
);
}),
placeholderData: keepPreviousData,
});

// members can add other members to a team if the `Open Membership` setting is enabled
// otherwise, `org:write` or `team:admin` permissions are required
Expand Down
18 changes: 8 additions & 10 deletions static/gsApp/views/contactBillingMembers.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import {Fragment} from 'react';
import {useQuery} from '@tanstack/react-query';

import {EmptyMessage} from 'sentry/components/emptyMessage';
import {Panel} from 'sentry/components/panels/panel';
import {IconWarning} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import type {Member} from 'sentry/types/organization';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {useApiQuery} from 'sentry/utils/queryClient';
import {apiOptions} from 'sentry/utils/api/apiOptions';
import {useOrganization} from 'sentry/utils/useOrganization';

function HelpfulMembers() {
const organization = useOrganization();
const {data: billingMembers} = useApiQuery<Member[]>(
[
getApiUrl('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: organization.slug},
}),
{query: {query: 'scope:"org:billing"'}},
],
{staleTime: 0}
const {data: billingMembers} = useQuery(
apiOptions.as<Member[]>()('/organizations/$organizationIdOrSlug/members/', {
path: {organizationIdOrSlug: organization.slug},
query: {query: 'scope:"org:billing"'},
staleTime: 0,
})
);

if (!billingMembers || billingMembers.length === 0) {
Expand Down
Loading