import {useEffect, useMemo} from 'react'; import ExternalLink from 'sentry/components/links/externalLink'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {t, tct} from 'sentry/locale'; import type {PageFilters} from 'sentry/types'; import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery'; import EventView from 'sentry/utils/discover/eventView'; import {DiscoverDatasets} from 'sentry/utils/discover/types'; import {usePageAlert} from 'sentry/utils/performance/contexts/pageAlert'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import usePrevious from 'sentry/utils/usePrevious'; import useProjects from 'sentry/utils/useProjects'; const MAX_PROJECTS_TO_LIST = 3; function getEventView(selection: PageFilters, compatibleSDKNames: string[]) { const {projects: selectedProjectIds} = selection; return EventView.fromNewQueryWithPageFilters( { dataset: DiscoverDatasets.SPANS_INDEXED, fields: ['project_id', 'sdk.name', 'count()'], projects: selectedProjectIds, query: `has:sdk.name !sdk.name:[${compatibleSDKNames.join(',')}]`, name: '', version: 2, }, selection ); } type PlatformCompatibilityCheckerProps = { children: React.ReactNode; compatibleSDKNames: string[]; docsUrl: string; }; // Checks if the currently selected platforms are compatible with the current view // and if there are incompatible projects, list them in a warning message. // If there are no compatible platforms, only return the alert, otherwise return the children // after setting the alert. export function PlatformCompatibilityChecker({ compatibleSDKNames, children, docsUrl, }: PlatformCompatibilityCheckerProps) { const location = useLocation(); const organization = useOrganization(); const {selection, isReady} = usePageFilters(); const {projects} = useProjects(); const {pageAlert, setPageWarning} = usePageAlert(); const {projects: selectedProjectIds} = selection; // Get the projects that are incompatible with the current view const {data, isLoading} = useDiscoverQuery({ eventView: getEventView(selection, compatibleSDKNames), location, orgSlug: organization.slug, referrer: 'api.starfish-mobile-platform-compatibility', options: { enabled: isReady, }, }); const incompatibleProjects = useMemo(() => { const incompatibleProjectIds = new Set(data?.data.map(row => row.project_id)); // My Projects and All Projects are represented by an empty selection // or -1. Don't filter in these cases const selectedProjects = selectedProjectIds.length === 0 || selectedProjectIds[0] === -1 ? projects : projects.filter(project => selectedProjectIds.includes(parseInt(project.id, 10)) ); return selectedProjects.filter(project => incompatibleProjectIds.has(parseInt(project.id, 10)) ); }, [data?.data, projects, selectedProjectIds]); const prevIncompatibleProjects = usePrevious(incompatibleProjects); useEffect(() => { if (incompatibleProjects.length > 0) { if (incompatibleProjects !== prevIncompatibleProjects) { const listedIncompatibleProjects = incompatibleProjects .slice(0, MAX_PROJECTS_TO_LIST) .map(project => project.slug) .join(', '); setPageWarning( tct( 'The following selected projects contain data from SDKs that are not supported by this view: [projects]. The currently supported SDK platforms are: [platforms]. We recommend to filter your current project selection to the supported platforms, to learn more, [link:read the docs].', { projects: incompatibleProjects.length <= MAX_PROJECTS_TO_LIST ? listedIncompatibleProjects : tct('[projects], and [count] more', { projects: listedIncompatibleProjects, count: incompatibleProjects.length - MAX_PROJECTS_TO_LIST, }), platforms: compatibleSDKNames.join(', '), link: {t('read the docs')}, } ) ); } } else if (incompatibleProjects.length === 0 && pageAlert?.message) { setPageWarning(undefined); } }, [ compatibleSDKNames, docsUrl, incompatibleProjects, pageAlert, prevIncompatibleProjects, setPageWarning, ]); if (isLoading) { return ; } if ( incompatibleProjects.length > 0 && incompatibleProjects.length === selectedProjectIds.length ) { // Don't return anything if all projects are incompatible return null; } return children; }