releaseContext.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import {useEffect} from 'react';
  2. import styled from '@emotion/styled';
  3. import AvatarList from 'sentry/components/avatar/avatarList';
  4. import {QuickContextCommitRow} from 'sentry/components/discover/quickContextCommitRow';
  5. import {DataSection} from 'sentry/components/events/styles';
  6. import Panel from 'sentry/components/panels/panel';
  7. import TimeSince from 'sentry/components/timeSince';
  8. import {IconNot} from 'sentry/icons';
  9. import {t, tct} from 'sentry/locale';
  10. import ConfigStore from 'sentry/stores/configStore';
  11. import {space} from 'sentry/styles/space';
  12. import {ReleaseWithHealth, User} from 'sentry/types';
  13. import {trackAnalytics} from 'sentry/utils/analytics';
  14. import {useApiQuery} from 'sentry/utils/queryClient';
  15. import {NoContext} from './quickContextWrapper';
  16. import {
  17. ContextBody,
  18. ContextContainer,
  19. ContextHeader,
  20. ContextRow,
  21. ContextTitle,
  22. Wrapper,
  23. } from './styles';
  24. import {BaseContextProps, ContextType, tenSecondInMs} from './utils';
  25. function ReleaseContext(props: BaseContextProps) {
  26. const {dataRow, organization} = props;
  27. const {isLoading, isError, data} = useApiQuery<ReleaseWithHealth>(
  28. [
  29. `/organizations/${organization.slug}/releases/${encodeURIComponent(
  30. dataRow.release
  31. )}/`,
  32. ],
  33. {
  34. staleTime: tenSecondInMs,
  35. }
  36. );
  37. useEffect(() => {
  38. trackAnalytics('discover_v2.quick_context_hover_contexts', {
  39. organization,
  40. contextType: ContextType.RELEASE,
  41. });
  42. }, [organization]);
  43. const getCommitAuthorTitle = () => {
  44. const user = ConfigStore.get('user');
  45. const commitCount = data?.commitCount || 0;
  46. let authorsCount = data?.authors?.length || 0;
  47. const userInAuthors =
  48. data &&
  49. authorsCount >= 1 &&
  50. data.authors.find((author: User) => author.id && user.id && author.id === user.id);
  51. if (userInAuthors) {
  52. authorsCount = authorsCount - 1;
  53. return authorsCount !== 1 && commitCount !== 1
  54. ? tct('[commitCount] commits by you and [authorsCount] others', {
  55. commitCount,
  56. authorsCount,
  57. })
  58. : commitCount !== 1
  59. ? tct('[commitCount] commits by you and 1 other', {
  60. commitCount,
  61. })
  62. : authorsCount !== 1
  63. ? tct('1 commit by you and [authorsCount] others', {
  64. authorsCount,
  65. })
  66. : t('1 commit by you and 1 other');
  67. }
  68. return (
  69. data &&
  70. (authorsCount !== 1 && commitCount !== 1
  71. ? tct('[commitCount] commits by [authorsCount] authors', {
  72. commitCount,
  73. authorsCount,
  74. })
  75. : commitCount !== 1
  76. ? tct('[commitCount] commits by 1 author', {
  77. commitCount,
  78. })
  79. : authorsCount !== 1
  80. ? tct('1 commit by [authorsCount] authors', {
  81. authorsCount,
  82. })
  83. : t('1 commit by 1 author'))
  84. );
  85. };
  86. const renderReleaseAuthors = () => {
  87. return (
  88. data && (
  89. <ReleaseContextContainer data-test-id="quick-context-release-details-container">
  90. <ContextHeader data-test-id="quick-context-release-author-header">
  91. <ContextTitle>{getCommitAuthorTitle()}</ContextTitle>
  92. </ContextHeader>
  93. <ContextBody>
  94. {data.commitCount === 0 ? (
  95. <IconNot color="gray500" size="md" />
  96. ) : (
  97. <StyledAvatarList users={data.authors} maxVisibleAvatars={10} />
  98. )}
  99. </ContextBody>
  100. </ReleaseContextContainer>
  101. )
  102. );
  103. };
  104. const renderLastCommit = () =>
  105. data &&
  106. data.lastCommit && (
  107. <ReleaseContextContainer data-test-id="quick-context-release-last-commit-container">
  108. <ContextHeader>
  109. <ContextTitle>{t('Last Commit')}</ContextTitle>
  110. </ContextHeader>
  111. <DataSection>
  112. <Panel>
  113. <QuickContextCommitRow commit={data.lastCommit} />
  114. </Panel>
  115. </DataSection>
  116. </ReleaseContextContainer>
  117. );
  118. const renderReleaseDetails = () =>
  119. data && (
  120. <ReleaseContextContainer data-test-id="quick-context-release-issues-and-authors-container">
  121. <ContextRow>
  122. <div>
  123. <ContextHeader>
  124. <ContextTitle>{t('Created')}</ContextTitle>
  125. </ContextHeader>
  126. <ReleaseBody>
  127. <TimeSince date={data.dateCreated} />
  128. </ReleaseBody>
  129. </div>
  130. <div>
  131. <ContextHeader>
  132. <ContextTitle>{t('Last Event')}</ContextTitle>
  133. </ContextHeader>
  134. <ReleaseBody>
  135. {data.lastEvent ? <TimeSince date={data.lastEvent} /> : '\u2014'}
  136. </ReleaseBody>
  137. </div>
  138. <div>
  139. <ContextHeader>
  140. <ContextTitle>{t('New Issues')}</ContextTitle>
  141. </ContextHeader>
  142. <ContextBody>{data.newGroups}</ContextBody>
  143. </div>
  144. </ContextRow>
  145. </ReleaseContextContainer>
  146. );
  147. if (isLoading || isError) {
  148. return <NoContext isLoading={isLoading} />;
  149. }
  150. return (
  151. <Wrapper data-test-id="quick-context-hover-body">
  152. {renderReleaseDetails()}
  153. {renderReleaseAuthors()}
  154. {renderLastCommit()}
  155. </Wrapper>
  156. );
  157. }
  158. const StyledAvatarList = styled(AvatarList)`
  159. margin: 0 ${space(0.75)};
  160. `;
  161. const ReleaseContextContainer = styled(ContextContainer)`
  162. ${Panel} {
  163. margin: 0;
  164. border: none;
  165. box-shadow: none;
  166. }
  167. ${DataSection} {
  168. padding: 0;
  169. }
  170. & + & {
  171. margin-top: ${space(2)};
  172. }
  173. `;
  174. const ReleaseBody = styled(ContextBody)<{}>`
  175. font-size: 13px;
  176. color: ${p => p.theme.subText};
  177. `;
  178. export default ReleaseContext;