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') {