filesChanged.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import {Fragment} from 'react';
  2. import * as Layout from 'sentry/components/layouts/thirds';
  3. import LoadingError from 'sentry/components/loadingError';
  4. import LoadingIndicator from 'sentry/components/loadingIndicator';
  5. import Pagination from 'sentry/components/pagination';
  6. import Panel from 'sentry/components/panels/panel';
  7. import PanelBody from 'sentry/components/panels/panelBody';
  8. import PanelHeader from 'sentry/components/panels/panelHeader';
  9. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  10. import {t, tn} from 'sentry/locale';
  11. import type {CommitFile, Repository} from 'sentry/types/integrations';
  12. import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
  13. import type {Organization} from 'sentry/types/organization';
  14. import type {Project} from 'sentry/types/project';
  15. import {useApiQuery} from 'sentry/utils/queryClient';
  16. import routeTitleGen from 'sentry/utils/routeTitle';
  17. import {useLocation} from 'sentry/utils/useLocation';
  18. import useOrganization from 'sentry/utils/useOrganization';
  19. import {useParams} from 'sentry/utils/useParams';
  20. import {formatVersion} from 'sentry/utils/versions/formatVersion';
  21. import {getFilesByRepository, getQuery, getReposToRender} from '../utils';
  22. import EmptyState from './emptyState';
  23. import FileChange from './fileChange';
  24. import RepositorySwitcher from './repositorySwitcher';
  25. import withReleaseRepos from './withReleaseRepos';
  26. // TODO(scttcper): Some props are no longer used, but required because of the HoC
  27. interface FilesChangedProps extends RouteComponentProps<{release: string}, {}> {
  28. orgSlug: Organization['slug'];
  29. projectSlug: Project['slug'];
  30. releaseRepos: Repository[];
  31. activeReleaseRepo?: Repository;
  32. }
  33. function FilesChanged({activeReleaseRepo, releaseRepos, projectSlug}: FilesChangedProps) {
  34. const location = useLocation();
  35. const params = useParams<{release: string}>();
  36. const organization = useOrganization();
  37. const query = getQuery({location, activeRepository: activeReleaseRepo});
  38. const {
  39. data: fileList = [],
  40. isPending: isLoadingFileList,
  41. error: fileListError,
  42. refetch,
  43. getResponseHeader,
  44. } = useApiQuery<CommitFile[]>(
  45. [
  46. `/organizations/${organization.slug}/releases/${encodeURIComponent(
  47. params.release
  48. )}/commitfiles/`,
  49. {query},
  50. ],
  51. {
  52. staleTime: Infinity,
  53. }
  54. );
  55. const filesByRepository = getFilesByRepository(fileList);
  56. const reposToRender = getReposToRender(Object.keys(filesByRepository));
  57. const fileListPageLinks = getResponseHeader?.('Link');
  58. return (
  59. <Fragment>
  60. <SentryDocumentTitle
  61. title={routeTitleGen(
  62. t('Files Changed - Release %s', formatVersion(params.release)),
  63. organization.slug,
  64. false,
  65. projectSlug
  66. )}
  67. />
  68. <Layout.Body>
  69. <Layout.Main fullWidth>
  70. {releaseRepos.length > 1 && (
  71. <RepositorySwitcher
  72. repositories={releaseRepos}
  73. activeRepository={activeReleaseRepo}
  74. />
  75. )}
  76. {fileListError && <LoadingError onRetry={refetch} />}
  77. {isLoadingFileList ? (
  78. <LoadingIndicator />
  79. ) : fileList.length ? (
  80. <Fragment>
  81. {reposToRender.map(repoName => {
  82. const repoData = filesByRepository[repoName];
  83. const files = Object.keys(repoData);
  84. const fileCount = files.length;
  85. return (
  86. <Panel key={repoName}>
  87. <PanelHeader>
  88. <span>{repoName}</span>
  89. <span>{tn('%s file changed', '%s files changed', fileCount)}</span>
  90. </PanelHeader>
  91. <PanelBody>
  92. {files.map(filename => {
  93. const {authors} = repoData[filename];
  94. return (
  95. <FileChange
  96. key={filename}
  97. filename={filename}
  98. authors={Object.values(authors)}
  99. />
  100. );
  101. })}
  102. </PanelBody>
  103. </Panel>
  104. );
  105. })}
  106. <Pagination pageLinks={fileListPageLinks} />
  107. </Fragment>
  108. ) : (
  109. <EmptyState>
  110. {activeReleaseRepo
  111. ? t(
  112. 'There are no changed files associated with this release in the %s repository.',
  113. activeReleaseRepo.name
  114. )
  115. : t('There are no changed files associated with this release.')}
  116. </EmptyState>
  117. )}
  118. </Layout.Main>
  119. </Layout.Body>
  120. </Fragment>
  121. );
  122. }
  123. export default withReleaseRepos(FilesChanged);