import {Fragment, useMemo} from 'react'; import {browserHistory} from 'react-router'; import styled from '@emotion/styled'; import {Location} from 'history'; import Pagination from 'sentry/components/pagination'; import {t, tct} from 'sentry/locale'; import type {Organization} from 'sentry/types'; import {trackAnalytics} from 'sentry/utils/analytics'; import EventView from 'sentry/utils/discover/eventView'; import {decodeScalar} from 'sentry/utils/queryString'; import {DEFAULT_SORT} from 'sentry/utils/replays/fetchReplayList'; import useReplayList from 'sentry/utils/replays/hooks/useReplayList'; import {useHaveSelectedProjectsSentAnyReplayEvents} from 'sentry/utils/replays/hooks/useReplayOnboarding'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import useProjectSdkNeedsUpdate from 'sentry/utils/useProjectSdkNeedsUpdate'; import ReplayOnboardingPanel from 'sentry/views/replays/list/replayOnboardingPanel'; import {ReplaySearchAlert} from 'sentry/views/replays/list/replaySearchAlert'; import ReplayTable from 'sentry/views/replays/replayTable'; import {ReplayColumns} from 'sentry/views/replays/replayTable/types'; import type {ReplayListLocationQuery} from 'sentry/views/replays/types'; import {REPLAY_LIST_FIELDS} from 'sentry/views/replays/types'; function ReplaysList() { const location = useLocation(); const organization = useOrganization(); const eventView = useMemo(() => { const query = decodeScalar(location.query.query, ''); const conditions = new MutableSearch(query); return EventView.fromNewQueryWithLocation( { id: '', name: '', version: 2, fields: REPLAY_LIST_FIELDS, projects: [], query: conditions.formatString(), orderby: decodeScalar(location.query.sort, DEFAULT_SORT), }, location ); }, [location]); const hasSessionReplay = organization.features.includes('session-replay'); const {hasSentOneReplay, fetching} = useHaveSelectedProjectsSentAnyReplayEvents(); return hasSessionReplay && !fetching && hasSentOneReplay ? ( ) : ( ); } const MIN_REPLAY_CLICK_SDK = '7.44.0'; function ReplaysListTable({ eventView, location, organization, }: { eventView: EventView; location: Location; organization: Organization; }) { const {replays, pageLinks, isFetching, fetchError} = useReplayList({ eventView, location, organization, }); const { selection: {projects}, } = usePageFilters(); const {needsUpdate: allSelectedProjectsNeedUpdates} = useProjectSdkNeedsUpdate({ minVersion: MIN_REPLAY_CLICK_SDK, organization, projectId: projects.map(p => String(p)), }); const conditions = useMemo(() => { return new MutableSearch(decodeScalar(location.query.query, '')); }, [location.query.query]); const hasReplayClick = conditions.getFilterKeys().some(k => k.startsWith('click.')); const hasReplayClickSearchBannerRollout = organization.features.includes( 'session-replay-click-search-banner-rollout' ); return ( {hasReplayClickSearchBannerRollout && ( )} {t('Unindexed search field')} {tct('Field [field] requires an [sdkPrompt]', { field: 'click', sdkPrompt: {t('SDK version >= 7.44.0')}, })} ) : undefined } /> { trackAnalytics('replay.list-paginated', { organization, direction: cursor?.endsWith(':1') ? 'prev' : 'next', }); browserHistory.push({ pathname: path, query: {...searchQuery, cursor}, }); }} /> ); } const EmptyStateSubheading = styled('div')` color: ${p => p.theme.subText}; font-size: ${p => p.theme.fontSizeMedium}; `; export default ReplaysList;