123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- 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<Release[]>(
- [
- `/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 <LoadingError />;
- }
- if (showLoadingIndicator) {
- return <Placeholder height={PLACEHOLDER_AND_EMPTY_HEIGHT} />;
- }
- if (!hasOlderReleases) {
- return (
- <MissingReleasesButtons
- organization={organization}
- projectId={project?.id}
- platform={project?.platform}
- />
- );
- }
- if (!releases || releases.length === 0) {
- return (
- <StyledEmptyStateWarning small>{t('No releases found')}</StyledEmptyStateWarning>
- );
- }
- return (
- <ReleasesTable>
- {releases.map(release => (
- <Fragment key={release.version}>
- <DateTime
- date={release.lastDeploy?.dateFinished || release.dateCreated}
- seconds={false}
- />
- <TextOverflow>
- <StyledVersion
- version={release.version}
- tooltipRawVersion
- projectId={project?.id}
- />
- </TextOverflow>
- </Fragment>
- ))}
- </ReleasesTable>
- );
- }
- function ProjectLatestReleases({
- isProjectStabilized,
- location,
- organization,
- projectSlug,
- project,
- }: Props) {
- const {
- data: releases = null,
- isLoading,
- isError,
- } = useApiQuery<Release[]>(
- [
- `/projects/${organization.slug}/${projectSlug}/releases/`,
- {
- query: {
- ...pick(location.query, Object.values(URL_PARAM)),
- per_page: 5,
- },
- },
- ],
- {
- staleTime: 0,
- enabled: isProjectStabilized,
- }
- );
- return (
- <SidebarSection>
- <SectionHeadingWrapper>
- <SectionHeading>{t('Latest Releases')}</SectionHeading>
- <SectionHeadingLink
- to={{
- pathname: makeReleasesPathname({
- organization,
- path: '/',
- }),
- query: {
- statsPeriod: undefined,
- start: undefined,
- end: undefined,
- utc: undefined,
- },
- }}
- >
- <IconOpen />
- </SectionHeadingLink>
- </SectionHeadingWrapper>
- <div>
- <ReleasesBody
- organization={organization}
- project={project}
- isProjectStabilized={isProjectStabilized}
- releases={releases}
- isLoading={isLoading}
- isError={isError}
- />
- </div>
- </SidebarSection>
- );
- }
- 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;
|