import {Fragment} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; import pick from 'lodash/pick'; import {SectionHeading} from 'sentry/components/charts/styles'; import {DateTime} from 'sentry/components/dateTime'; import EmptyStateWarning from 'sentry/components/emptyStateWarning'; import LoadingError from 'sentry/components/loadingError'; import Placeholder from 'sentry/components/placeholder'; import TextOverflow from 'sentry/components/textOverflow'; import Version from 'sentry/components/version'; import {URL_PARAM} from 'sentry/constants/pageFilters'; import {IconOpen} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import type {Release} from 'sentry/types/release'; import {useApiQuery} from 'sentry/utils/queryClient'; import {makeReleasesPathname} from 'sentry/views/releases/utils/pathnames'; import MissingReleasesButtons from './missingFeatureButtons/missingReleasesButtons'; import {SectionHeadingLink, SectionHeadingWrapper, SidebarSection} from './styles'; const PLACEHOLDER_AND_EMPTY_HEIGHT = '160px'; type Props = { isProjectStabilized: boolean; location: Location; organization: Organization; projectSlug: string; project?: Project; }; type BodyProps = { isError: boolean; isLoading: boolean; isProjectStabilized: boolean; organization: Organization; project: Project | undefined; releases: Release[] | null; }; function useHasOlderReleases({ releases, releasesLoading, organization, project, isProjectStabilized, }: { isProjectStabilized: boolean; organization: Organization; project: Project | undefined; releases: Release[] | null; releasesLoading: boolean; }) { const skipOldReleaseCheck = releasesLoading || (releases ?? []).length !== 0 || !project?.id || !isProjectStabilized; const {data: olderReleases, isPending} = useApiQuery( [ `/organizations/${organization.slug}/releases/stats/`, { query: { statsPeriod: '90d', project: project?.id, per_page: 1, }, }, ], {staleTime: 0, enabled: !skipOldReleaseCheck} ); if (skipOldReleaseCheck) { return true; } if (isPending) { return null; } return (olderReleases?.length ?? 0) > 0; } function ReleasesBody({ organization, project, isProjectStabilized, releases, isLoading, isError, }: BodyProps) { const hasOlderReleases = useHasOlderReleases({ organization, project, releases, releasesLoading: isLoading, isProjectStabilized, }); const checkingForOlderReleases = !(releases ?? []).length && hasOlderReleases === null; const showLoadingIndicator = isLoading || checkingForOlderReleases || !isProjectStabilized; if (isError) { return ; } if (showLoadingIndicator) { return ; } if (!hasOlderReleases) { return ( ); } if (!releases || releases.length === 0) { return ( {t('No releases found')} ); } return ( {releases.map(release => ( ))} ); } function ProjectLatestReleases({ isProjectStabilized, location, organization, projectSlug, project, }: Props) { const { data: releases = null, isLoading, isError, } = useApiQuery( [ `/projects/${organization.slug}/${projectSlug}/releases/`, { query: { ...pick(location.query, Object.values(URL_PARAM)), per_page: 5, }, }, ], { staleTime: 0, enabled: isProjectStabilized, } ); return ( {t('Latest Releases')}
); } const ReleasesTable = styled('div')` display: grid; font-size: ${p => p.theme.fontSizeMedium}; white-space: nowrap; grid-template-columns: 1fr auto; margin-bottom: ${space(2)}; & > * { padding: ${space(0.5)} ${space(1)}; height: 32px; } & > *:nth-child(2n + 2) { text-align: right; } & > *:nth-child(4n + 1), & > *:nth-child(4n + 2) { background-color: ${p => p.theme.rowBackground}; } `; const StyledVersion = styled(Version)` ${p => p.theme.overflowEllipsis} line-height: 1.6; font-variant-numeric: tabular-nums; `; const StyledEmptyStateWarning = styled(EmptyStateWarning)` height: ${PLACEHOLDER_AND_EMPTY_HEIGHT}; justify-content: center; `; export default ProjectLatestReleases;