filesChanged.tsx 4.6 KB

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