diff --git a/static/app/components/feedback/list/feedbackList.tsx b/static/app/components/feedback/list/feedbackList.tsx
index 2bf0cebea0ea..07bf6f3837c8 100644
--- a/static/app/components/feedback/list/feedbackList.tsx
+++ b/static/app/components/feedback/list/feedbackList.tsx
@@ -1,4 +1,4 @@
-import {Fragment, useMemo} from 'react';
+import {useMemo} from 'react';
import styled from '@emotion/styled';
import {useInfiniteQuery} from '@tanstack/react-query';
import uniqBy from 'lodash/uniqBy';
@@ -17,8 +17,9 @@ import {InfiniteListState} from 'sentry/components/infiniteList/infiniteListStat
import {LoadingIndicator} from 'sentry/components/loadingIndicator';
import {t} from 'sentry/locale';
import type {ApiResponse} from 'sentry/utils/api/apiFetch';
+import {safeParseQueryKey} from 'sentry/utils/api/apiQueryKey';
import type {FeedbackIssueListItem} from 'sentry/utils/feedback/types';
-import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
+import {ListItemCheckboxProvider} from 'sentry/utils/list/useListItemCheckboxState';
function NoFeedback() {
return (
@@ -43,15 +44,14 @@ export function FeedbackList({onItemSelect}: Props) {
() => uniqBy(queryResult.data?.pages.flatMap(page => page.json) ?? [], 'id'),
[queryResult.data?.pages]
);
- const checkboxState = useListItemCheckboxContext({
- hits: Number(queryResult.data?.pages[0]?.headers['X-Hits'] ?? issues.length),
- knownIds: issues.map(issue => issue.id),
- queryKey: listApiOptions.queryKey,
- });
return (
-
-
+ issue.id)}
+ endpointOptions={safeParseQueryKey(listApiOptions.queryKey)?.options}
+ >
+
{
- checkboxState.toggleSelected(item.id);
- }}
onItemSelect={() => onItemSelect(itemIndex)}
/>
@@ -94,7 +90,7 @@ export function FeedbackList({onItemSelect}: Props) {
/>
-
+
);
}
diff --git a/static/app/components/feedback/list/feedbackListHeader.tsx b/static/app/components/feedback/list/feedbackListHeader.tsx
index 7113d6014fb6..30852a756056 100644
--- a/static/app/components/feedback/list/feedbackListHeader.tsx
+++ b/static/app/components/feedback/list/feedbackListHeader.tsx
@@ -12,26 +12,17 @@ import {useFeedbackHasNewItems} from 'sentry/components/feedback/useFeedbackHasN
import {useMailbox} from 'sentry/components/feedback/useMailbox';
import {IconRefresh} from 'sentry/icons';
import {t} from 'sentry/locale';
-import type {ListItemCheckboxState} from 'sentry/utils/list/useListItemCheckboxState';
+import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
-interface Props extends Pick<
- ListItemCheckboxState,
- | 'countSelected'
- | 'deselectAll'
- | 'isAllSelected'
- | 'isAnySelected'
- | 'selectAll'
- | 'selectedIds'
-> {}
-
-export function FeedbackListHeader({
- countSelected,
- deselectAll,
- isAllSelected,
- isAnySelected,
- selectAll,
- selectedIds,
-}: Props) {
+export function FeedbackListHeader() {
+ const {
+ countSelected,
+ deselectAll,
+ isAllSelected,
+ isAnySelected,
+ selectAll,
+ selectedIds,
+ } = useListItemCheckboxContext();
const [mailbox, setMailbox] = useMailbox();
const {listPrefetchApiOptions, resetListHeadTime} = useFeedbackApiOptions();
diff --git a/static/app/components/feedback/list/feedbackListItem.tsx b/static/app/components/feedback/list/feedbackListItem.tsx
index cd8e9a397177..61888c628419 100644
--- a/static/app/components/feedback/list/feedbackListItem.tsx
+++ b/static/app/components/feedback/list/feedbackListItem.tsx
@@ -17,6 +17,7 @@ import type {Group} from 'sentry/types/group';
import {trackAnalytics} from 'sentry/utils/analytics';
import {feedbackHasLinkedError} from 'sentry/utils/feedback/hasLinkedError';
import {type FeedbackIssueListItem} from 'sentry/utils/feedback/types';
+import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
import {decodeScalar} from 'sentry/utils/queryString';
import {useReplayCountForFeedbacks} from 'sentry/utils/replayCount/useReplayCountForFeedbacks';
import {useLocationQuery} from 'sentry/utils/url/useLocationQuery';
@@ -26,9 +27,7 @@ import {makeFeedbackPathname} from 'sentry/views/feedback/pathnames';
interface Props {
feedbackItem: FeedbackIssueListItem;
- isSelected: 'all-selected' | boolean;
onItemSelect: () => void;
- onSelect: (isSelected: boolean) => void;
}
function useIsSelectedFeedback({feedbackItem}: {feedbackItem: FeedbackIssueListItem}) {
@@ -39,12 +38,9 @@ function useIsSelectedFeedback({feedbackItem}: {feedbackItem: FeedbackIssueListI
return feedbackId === feedbackItem.id;
}
-export function FeedbackListItem({
- feedbackItem,
- isSelected,
- onSelect,
- onItemSelect,
-}: Props) {
+export function FeedbackListItem({feedbackItem, onItemSelect}: Props) {
+ const {isSelected, toggleSelected} = useListItemCheckboxContext();
+
const organization = useOrganization();
const isOpen = useIsSelectedFeedback({feedbackItem});
const {feedbackHasReplay} = useReplayCountForFeedbacks();
@@ -84,10 +80,10 @@ export function FeedbackListItem({
}}
>
{
- onSelect(e.target.checked);
+ disabled={isSelected(feedbackItem.id) === 'all-selected'}
+ checked={isSelected(feedbackItem.id) !== false}
+ onChange={() => {
+ toggleSelected(feedbackItem.id);
}}
/>
diff --git a/static/app/components/replays/table/replayBulkViewedActions.spec.tsx b/static/app/components/replays/table/replayBulkViewedActions.spec.tsx
index eec077618c33..87b35c587529 100644
--- a/static/app/components/replays/table/replayBulkViewedActions.spec.tsx
+++ b/static/app/components/replays/table/replayBulkViewedActions.spec.tsx
@@ -6,8 +6,6 @@ import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrar
import {ReplayBulkViewedActions} from 'sentry/components/replays/table/replayBulkViewedActions';
import {ProjectsStore} from 'sentry/stores/projectsStore';
import {trackAnalytics} from 'sentry/utils/analytics';
-import type {ApiQueryKey} from 'sentry/utils/api/apiQueryKey';
-import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import type {ListCheckboxQueryKeyRef} from 'sentry/utils/list/useListItemCheckboxState';
import type {ReplayListRecord} from 'sentry/views/explore/replays/types';
@@ -51,22 +49,11 @@ describe('ReplayBulkViewedActions', () => {
const replay = createReplay();
const deselectAll = jest.fn();
- const apiUrl = getApiUrl(
- '/projects/$organizationIdOrSlug/$projectIdOrSlug/replays/$replayId/viewed-by/',
- {
- path: {
- organizationIdOrSlug: organization.id,
- projectIdOrSlug: 2,
- replayId: replay.id,
- },
- }
- );
- const queryKey: ApiQueryKey = [apiUrl, {}, {infinite: false}];
- const queryKeyRef = {current: queryKey};
+ const endpointOptionsRef: ListCheckboxQueryKeyRef = {current: {query: {}}};
const renderWithOrganization = (
overrides: {
- queryKeyRef?: ListCheckboxQueryKeyRef;
+ endpointOptionsRef?: ListCheckboxQueryKeyRef;
replays?: ReplayListRecord[];
selectedIds?: string[];
} = {}
@@ -74,7 +61,7 @@ describe('ReplayBulkViewedActions', () => {
render(
,
diff --git a/static/app/components/replays/table/replayBulkViewedActions.tsx b/static/app/components/replays/table/replayBulkViewedActions.tsx
index ce092e012959..e4b1be7c387a 100644
--- a/static/app/components/replays/table/replayBulkViewedActions.tsx
+++ b/static/app/components/replays/table/replayBulkViewedActions.tsx
@@ -8,18 +8,15 @@ import {LoadingIndicator} from 'sentry/components/loadingIndicator';
import {IconCheckmark} from 'sentry/icons';
import {t, tn} from 'sentry/locale';
import {trackAnalytics} from 'sentry/utils/analytics';
-import type {ApiResponse} from 'sentry/utils/api/apiFetch';
import type {ListCheckboxQueryKeyRef} from 'sentry/utils/list/useListItemCheckboxState';
import {fetchMutation} from 'sentry/utils/queryClient';
+import {replayListApiOptions} from 'sentry/utils/replays/replayListApiOptions';
import {useOrganization} from 'sentry/utils/useOrganization';
-import type {
- HydratedReplayRecord,
- ReplayListRecord,
-} from 'sentry/views/explore/replays/types';
+import type {ReplayListRecord} from 'sentry/views/explore/replays/types';
interface Props {
deselectAll: () => void;
- queryKeyRef: ListCheckboxQueryKeyRef;
+ endpointOptionsRef: ListCheckboxQueryKeyRef;
replays: ReplayListRecord[];
selectedIds: string[];
}
@@ -28,7 +25,7 @@ export function ReplayBulkViewedActions({
deselectAll,
replays,
selectedIds,
- queryKeyRef,
+ endpointOptionsRef,
}: Props) {
const organization = useOrganization();
const queryClient = useQueryClient();
@@ -55,10 +52,14 @@ export function ReplayBulkViewedActions({
);
if (succeededIds.size) {
- if (queryKeyRef.current) {
- // eslint-disable-next-line @sentry/no-query-data-type-parameters
- queryClient.setQueryData>(
- queryKeyRef.current,
+ if (endpointOptionsRef.current) {
+ const replayListOptions = replayListApiOptions({
+ options: endpointOptionsRef.current,
+ organization,
+ queryReferrer: 'replayList',
+ });
+ queryClient.setQueryData(
+ replayListOptions.queryKey,
old =>
old && {
...old,
diff --git a/static/app/components/replays/table/replayTableHeader.spec.tsx b/static/app/components/replays/table/replayTableHeader.spec.tsx
index c82ed934d1c8..8dfe977b22e3 100644
--- a/static/app/components/replays/table/replayTableHeader.spec.tsx
+++ b/static/app/components/replays/table/replayTableHeader.spec.tsx
@@ -30,7 +30,7 @@ function baseListCheckboxState(overrides: Partial) {
isAnySelected: false,
isSelected: () => false,
knownIds: ['a', 'b'],
- queryKeyRef: {current: undefined},
+ endpointOptionsRef: {current: undefined},
selectAll: jest.fn(),
selectedIds: [],
toggleSelected: jest.fn(),
diff --git a/static/app/components/replays/table/replayTableHeader.tsx b/static/app/components/replays/table/replayTableHeader.tsx
index c4ca2cc931af..6b2a769a0bbe 100644
--- a/static/app/components/replays/table/replayTableHeader.tsx
+++ b/static/app/components/replays/table/replayTableHeader.tsx
@@ -12,7 +12,6 @@ import {
} from 'sentry/components/replays/table/replayTableColumns';
import {SimpleTable} from 'sentry/components/tables/simpleTable';
import {t, tct, tn} from 'sentry/locale';
-import {parseQueryKey} from 'sentry/utils/api/apiQueryKey';
import type {Sort} from 'sentry/utils/discover/fields';
import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
import type {ReplayListRecord} from 'sentry/views/explore/replays/types';
@@ -38,14 +37,12 @@ export function ReplayTableHeader({
deselectAll,
isAllSelected,
isAnySelected,
- queryKeyRef,
+ endpointOptionsRef,
selectAll,
selectedIds,
} = listItemCheckboxState;
- const queryOptions = queryKeyRef.current
- ? parseQueryKey(queryKeyRef.current).options
- : undefined;
- const queryString = queryOptions?.query?.query as string | undefined;
+ const endpointOptions = endpointOptionsRef.current;
+ const queryString = endpointOptions?.query?.query as string | undefined;
const headerStyle: React.CSSProperties = stickyHeader
? {position: 'sticky', top: 0}
@@ -89,13 +86,13 @@ export function ReplayTableHeader({
{selectedIds !== 'all' && (
)}
diff --git a/static/app/utils/list/useListItemCheckboxState.spec.ts b/static/app/utils/list/useListItemCheckboxState.spec.tsx
similarity index 84%
rename from static/app/utils/list/useListItemCheckboxState.spec.ts
rename to static/app/utils/list/useListItemCheckboxState.spec.tsx
index 82e2cd616fa2..f94b1b74fd7c 100644
--- a/static/app/utils/list/useListItemCheckboxState.spec.ts
+++ b/static/app/utils/list/useListItemCheckboxState.spec.tsx
@@ -1,17 +1,33 @@
+import {type ComponentProps, type PropsWithChildren} from 'react';
+
import {act, renderHook} from 'sentry-test/reactTestingLibrary';
-import {getApiUrl} from 'sentry/utils/api/getApiUrl';
-import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
-import type {ApiQueryKey} from 'sentry/utils/queryClient';
+import type {QueryKeyEndpointOptions} from 'sentry/utils/api/apiQueryKey';
+import {
+ ListItemCheckboxProvider,
+ useListItemCheckboxContext,
+} from 'sentry/utils/list/useListItemCheckboxState';
+
+const endpointOptions: QueryKeyEndpointOptions = {query: {status: 'active'}};
+
+type ProviderProps = ComponentProps;
-const queryKey: ApiQueryKey = [getApiUrl('/api-tokens/')];
+function createWrapper(props: Omit) {
+ return function Wrapper({children}: PropsWithChildren) {
+ return (
+
+ {children}
+
+ );
+ };
+}
describe('useListItemCheckboxContext', () => {
describe('All hits are already known', () => {
it('should return the correct initial state', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 3, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 3, knownIds: ['1', '2', '3']}),
+ });
expect(result.current).toEqual({
countSelected: 0,
deselectAll: expect.any(Function),
@@ -20,7 +36,7 @@ describe('useListItemCheckboxContext', () => {
isAnySelected: false,
isSelected: expect.any(Function),
knownIds: ['1', '2', '3'],
- queryKeyRef: {current: queryKey},
+ endpointOptionsRef: {current: endpointOptions},
selectAll: expect.any(Function),
selectedIds: [],
toggleSelected: expect.any(Function),
@@ -28,9 +44,9 @@ describe('useListItemCheckboxContext', () => {
});
it('should allow selecting an individual item when all hits are known', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 3, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 3, knownIds: ['1', '2', '3']}),
+ });
// Initially nothing is selected
expect(result.current.isSelected('1')).toBe(false);
@@ -84,9 +100,9 @@ describe('useListItemCheckboxContext', () => {
});
it('sets isAllSelected to true when all items are selected', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 3, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 3, knownIds: ['1', '2', '3']}),
+ });
// Initially nothing is selected
expect(result.current.isSelected('1')).toBe(false);
@@ -108,9 +124,9 @@ describe('useListItemCheckboxContext', () => {
});
it('should allow selecting all items with selectAll', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 3, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 3, knownIds: ['1', '2', '3']}),
+ });
// Initially nothing is selected
expect(result.current.isSelected('1')).toBe(false);
@@ -152,9 +168,9 @@ describe('useListItemCheckboxContext', () => {
describe('More hits to load', () => {
it('should return the correct initial state', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 10, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 10, knownIds: ['1', '2', '3']}),
+ });
expect(result.current).toEqual({
countSelected: 0,
deselectAll: expect.any(Function),
@@ -163,7 +179,7 @@ describe('useListItemCheckboxContext', () => {
isAnySelected: false,
isSelected: expect.any(Function),
knownIds: ['1', '2', '3'],
- queryKeyRef: {current: queryKey},
+ endpointOptionsRef: {current: endpointOptions},
selectAll: expect.any(Function),
selectedIds: [],
toggleSelected: expect.any(Function),
@@ -171,9 +187,9 @@ describe('useListItemCheckboxContext', () => {
});
it('should allow selecting individual items when there are more hits to load', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 10, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 10, knownIds: ['1', '2', '3']}),
+ });
// Initially nothing is selected
expect(result.current.isSelected('1')).toBe(false);
@@ -241,9 +257,9 @@ describe('useListItemCheckboxContext', () => {
});
it('should allow selecting all items with selectAll', () => {
- const {result} = renderHook(() =>
- useListItemCheckboxContext({hits: 10, knownIds: ['1', '2', '3'], queryKey})
- );
+ const {result} = renderHook(useListItemCheckboxContext, {
+ wrapper: createWrapper({hits: 10, knownIds: ['1', '2', '3']}),
+ });
// Initially nothing is selected
expect(result.current.isSelected('1')).toBe(false);
diff --git a/static/app/utils/list/useListItemCheckboxState.tsx b/static/app/utils/list/useListItemCheckboxState.tsx
index a99f1529e51b..5a3f24dae481 100644
--- a/static/app/utils/list/useListItemCheckboxState.tsx
+++ b/static/app/utils/list/useListItemCheckboxState.tsx
@@ -9,13 +9,23 @@ import {
useState,
} from 'react';
+import type {QueryKeyEndpointOptions} from 'sentry/utils/api/apiQueryKey';
import {toArray} from 'sentry/utils/array/toArray';
-import type {ApiQueryKey, InfiniteApiQueryKey} from 'sentry/utils/queryClient';
-type ListCheckboxQueryKeyValue = undefined | ApiQueryKey | InfiniteApiQueryKey;
-export type ListCheckboxQueryKeyRef = RefObject;
+export type ListCheckboxQueryKeyRef = RefObject;
interface PublicProps {
+ /**
+ * The cache-key that identifies the query & results.
+ *
+ * When the key changes, the selection will be reset. Therefore be
+ * mindful of query params like `cursor` creating a new query key.
+ *
+ * This is not the same as ApiQueryKey. But you could use:
+ * `safeParseQueryKey(apiQueryKey)?.options`.
+ */
+ endpointOptions: undefined | QueryKeyEndpointOptions;
+
/**
* The total number of items the query could return
*/
@@ -25,22 +35,14 @@ interface PublicProps {
* The number of items that are currently loaded into the browser
*/
knownIds: string[];
-
- /**
- * The query key that fetches the list.
- *
- * The selection will be reset when then query key changes. Therefore be
- * mindful of query params like `cursor` creating a new query key.
- */
- queryKey: ListCheckboxQueryKeyValue;
}
interface InternalProps {
- queryKeyRef: ListCheckboxQueryKeyRef;
+ endpointOptionsRef: ListCheckboxQueryKeyRef;
setState: Dispatch>;
state: State;
}
-type MergedProps = Omit & InternalProps;
+type MergedProps = Omit & InternalProps;
/**
* We can either have a list of ids, or have all selected.
@@ -59,6 +61,13 @@ export interface ListItemCheckboxState {
*/
deselectAll: () => void;
+ /**
+ * Stable ref to the query key that fetches the list.
+ *
+ * Read `.current` to access the value.
+ */
+ endpointOptionsRef: ListCheckboxQueryKeyRef;
+
/**
* The total number of items the query could return
*/
@@ -92,13 +101,6 @@ export interface ListItemCheckboxState {
*/
knownIds: string[];
- /**
- * Stable ref to the query key that fetches the list.
- *
- * Read `.current` to access the value.
- */
- queryKeyRef: ListCheckboxQueryKeyRef;
-
/**
* Record that all are selected, wether or not all feedback ids are loaded or not
*/
@@ -118,81 +120,46 @@ export interface ListItemCheckboxState {
const defaultQueryKeyRef: ListCheckboxQueryKeyRef = {current: undefined};
-const ListItemCheckboxContext = createContext<{
- hits: number;
- knownIds: string[];
- queryKeyRef: ListCheckboxQueryKeyRef;
- setState?: Dispatch>;
- state?: State;
-}>({
+const ListItemCheckboxContext = createContext({
hits: 0,
knownIds: [],
- queryKeyRef: defaultQueryKeyRef,
+ endpointOptionsRef: defaultQueryKeyRef,
+ setState: () => {},
+ state: {ids: new Set()},
});
export function ListItemCheckboxProvider({
children,
hits,
knownIds,
- queryKey,
+ endpointOptions,
}: {
children: React.ReactNode;
- hits: number;
- knownIds: string[];
- queryKey: ListCheckboxQueryKeyValue;
-}) {
+} & PublicProps) {
const [state, setState] = useState({ids: new Set()});
- const queryKeyRef = useRef(queryKey);
+ const endpointOptionsRef = useRef(endpointOptions);
- const serializedQueryKey = JSON.stringify(queryKey);
+ const serializedEndpointOptions = JSON.stringify(endpointOptions);
useEffect(() => {
- queryKeyRef.current = queryKey;
+ endpointOptionsRef.current = endpointOptions;
setState({ids: new Set()});
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [serializedQueryKey]);
+ }, [serializedEndpointOptions]);
return (
{children}
);
}
-export function useListItemCheckboxContext(props?: PublicProps) {
- const [localState, localSetState] = useState({ids: new Set()});
- const context = useContext(ListItemCheckboxContext);
-
- const setState = context.setState ?? localSetState;
- const state = context.state ?? localState;
-
- const localQueryKeyRef = useRef(props?.queryKey);
- const serializedPropsKey = JSON.stringify(props?.queryKey);
- useEffect(() => {
- if (props?.queryKey !== undefined) {
- localQueryKeyRef.current = props.queryKey;
- setState({ids: new Set()});
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [serializedPropsKey]);
-
- return useListItemCheckboxState({
- state,
- setState,
- hits: props?.hits ?? context.hits,
- knownIds: props?.knownIds ?? context.knownIds,
- queryKeyRef: props?.queryKey === undefined ? context.queryKeyRef : localQueryKeyRef,
- });
-}
+export function useListItemCheckboxContext(): ListItemCheckboxState {
+ const {state, setState, hits, knownIds, endpointOptionsRef} = useContext(
+ ListItemCheckboxContext
+ );
-function useListItemCheckboxState({
- hits,
- knownIds,
- queryKeyRef,
- state,
- setState,
-}: MergedProps): ListItemCheckboxState {
const selectAll = useCallback(() => {
// Record that the virtual "all" list is enabled.
setState({all: true});
@@ -281,7 +248,7 @@ function useListItemCheckboxState({
isAnySelected,
isSelected,
knownIds,
- queryKeyRef,
+ endpointOptionsRef,
selectAll,
selectedIds,
toggleSelected,
diff --git a/static/app/utils/queryClient.tsx b/static/app/utils/queryClient.tsx
index bd599969c4f8..a706a7887a8a 100644
--- a/static/app/utils/queryClient.tsx
+++ b/static/app/utils/queryClient.tsx
@@ -13,11 +13,7 @@ import type {ApiResponse} from 'sentry/utils/api/apiFetch';
import {apiFetch} from 'sentry/utils/api/apiFetch';
import {selectJson} from 'sentry/utils/api/apiOptions';
import {normalizeQueryKey} from 'sentry/utils/api/apiQueryKey';
-import type {
- ApiQueryKey,
- InfiniteApiQueryKey,
- QueryKeyEndpointOptions,
-} from 'sentry/utils/api/apiQueryKey';
+import type {ApiQueryKey, QueryKeyEndpointOptions} from 'sentry/utils/api/apiQueryKey';
import type {RequestError} from 'sentry/utils/requestError/requestError';
export type {
@@ -25,10 +21,6 @@ export type {
* @deprecated Use `import type {ApiQueryKey} from 'sentry/utils/api/apiQueryKey';` directly instead.
*/
ApiQueryKey,
- /**
- * @deprecated Use `import type {InfiniteApiQueryKey} from 'sentry/utils/api/apiQueryKey';` directlyinstead.
- */
- InfiniteApiQueryKey,
/**
* @deprecated Use `import type {QueryKeyEndpointOptions} from 'sentry/utils/api/apiQueryKey';` directly instead.
*/
diff --git a/static/app/views/explore/replays/list/replayIndexTable.tsx b/static/app/views/explore/replays/list/replayIndexTable.tsx
index 499623e6e3f5..fd2c9b646f6e 100644
--- a/static/app/views/explore/replays/list/replayIndexTable.tsx
+++ b/static/app/views/explore/replays/list/replayIndexTable.tsx
@@ -11,7 +11,7 @@ import {ReplayTable} from 'sentry/components/replays/table/replayTable';
import {useReplayTableSort} from 'sentry/components/replays/table/useReplayTableSort';
import {usePlaylistQuery} from 'sentry/components/replays/usePlaylistQuery';
import {t, tct} from 'sentry/locale';
-import {type ApiQueryKey, parseQueryKey} from 'sentry/utils/api/apiQueryKey';
+import {type ApiQueryKey, safeParseQueryKey} from 'sentry/utils/api/apiQueryKey';
import {ListItemCheckboxProvider} from 'sentry/utils/list/useListItemCheckboxState';
import {MIN_REPLAY_CLICK_SDK} from 'sentry/utils/replays/sdkVersions';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
@@ -56,9 +56,9 @@ export function ReplayIndexTable({
const {allMobileProj} = useAllMobileProj({});
const columns = useReplayIndexTableColumns({allMobileProj, tableDimensions});
- const {options} = parseQueryKey(queryKey);
+ const endpointOptions = safeParseQueryKey(queryKey)?.options;
const needsSDKUpdateForClickSearch = useNeedsSDKUpdateForClickSearch({
- search: options?.query?.query as string | undefined,
+ search: endpointOptions?.query?.query as string | undefined,
});
const needsJetpackComposePiiWarning = useNeedsJetpackComposePiiNotice({
@@ -82,7 +82,7 @@ export function ReplayIndexTable({
replay.id)}
- queryKey={queryKey}
+ endpointOptions={endpointOptions}
>
{needsSDKUpdateForClickSearch ? (
diff --git a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTable.tsx b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTable.tsx
index 3fb658852079..3df0dc3d3968 100644
--- a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTable.tsx
+++ b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTable.tsx
@@ -34,7 +34,6 @@ import {t, tct} from 'sentry/locale';
import {ProjectsStore} from 'sentry/stores/projectsStore';
import {useFetchAllPages} from 'sentry/utils/api/apiFetch';
import {ListItemCheckboxProvider} from 'sentry/utils/list/useListItemCheckboxState';
-import type {ApiQueryKey} from 'sentry/utils/queryClient';
import {getCodingAgentSelectQueryOptions} from 'sentry/utils/seer/preferredAgent';
import {
getFilteredCodingAgentName,
@@ -166,11 +165,6 @@ export function SeerProjectTable() {
parseAsSort.withDefault({field: 'project', kind: 'asc'})
);
- const queryKey = [
- 'seer-projects',
- {query: {query: searchTerm, sort, agent: agentFilter}},
- ] as unknown as ApiQueryKey;
-
const sortedProjects = useMemo(() => {
return projects.toSorted((a, b) => {
if (sort.field === 'project') {
@@ -273,7 +267,9 @@ export function SeerProjectTable() {
project.id)}
- queryKey={queryKey}
+ endpointOptions={{
+ query: {query: searchTerm, sort, agent: agentFilter},
+ }}
>
diff --git a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx
index 50d7de9d2389..70984a187f09 100644
--- a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx
+++ b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx
@@ -15,7 +15,6 @@ import {SimpleTable} from 'sentry/components/tables/simpleTable';
import {t, tct, tn} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
import type {Project} from 'sentry/types/project';
-import {parseQueryKey} from 'sentry/utils/api/apiQueryKey';
import type {Sort} from 'sentry/utils/discover/fields';
import {
useListItemCheckboxContext,
@@ -114,14 +113,12 @@ export function ProjectTableHeader({
countSelected,
isAllSelected,
isAnySelected,
- queryKeyRef,
+ endpointOptionsRef,
selectAll,
selectedIds,
} = listItemCheckboxState;
- const queryOptions = queryKeyRef.current
- ? parseQueryKey(queryKeyRef.current).options
- : undefined;
- const queryString = queryOptions?.query?.query as string | undefined;
+ const endpointOptions = endpointOptionsRef.current;
+ const queryString = endpointOptions?.query?.query as string | undefined;
const projectIds = useMemo(
() => (selectedIds === 'all' ? projects.map(project => project.id) : selectedIds),
diff --git a/static/gsApp/views/seerAutomation/components/repoTable/seerRepoTable.tsx b/static/gsApp/views/seerAutomation/components/repoTable/seerRepoTable.tsx
index d438ba4aaaa8..29e2629b30ff 100644
--- a/static/gsApp/views/seerAutomation/components/repoTable/seerRepoTable.tsx
+++ b/static/gsApp/views/seerAutomation/components/repoTable/seerRepoTable.tsx
@@ -24,6 +24,7 @@ import {IconSearch} from 'sentry/icons/iconSearch';
import {t, tct} from 'sentry/locale';
import type {RepositoryWithSettings} from 'sentry/types/integrations';
import {useFetchAllPages} from 'sentry/utils/api/apiFetch';
+import {safeParseQueryKey} from 'sentry/utils/api/apiQueryKey';
import {getSeerOnboardingCheckQueryOptions} from 'sentry/utils/getSeerOnboardingCheckQueryOptions';
import {
ListItemCheckboxProvider,
@@ -200,7 +201,7 @@ export function SeerRepoTable() {
{
if (selectedIds === 'all') {