releaseContext.tsx 5.6 KB

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