resolutionBox.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import UserAvatar from 'sentry/components/avatar/userAvatar';
  4. import CommitLink from 'sentry/components/commitLink';
  5. import {BannerContainer, BannerSummary} from 'sentry/components/events/styles';
  6. import TimeSince from 'sentry/components/timeSince';
  7. import Version from 'sentry/components/version';
  8. import {IconCheckmark} from 'sentry/icons';
  9. import {t, tct} from 'sentry/locale';
  10. import {space} from 'sentry/styles/space';
  11. import {
  12. GroupActivity,
  13. GroupActivitySetByResolvedInNextSemverRelease,
  14. GroupActivitySetByResolvedInRelease,
  15. GroupActivityType,
  16. Repository,
  17. ResolvedStatusDetails,
  18. } from 'sentry/types';
  19. type Props = {
  20. projectId: string;
  21. // TODO(ts): This should be a union type `IgnoredStatusDetails | ResolvedStatusDetails`
  22. statusDetails: ResolvedStatusDetails;
  23. activities?: GroupActivity[];
  24. };
  25. function renderReason(
  26. statusDetails: ResolvedStatusDetails,
  27. projectId: string,
  28. activities: GroupActivity[]
  29. ) {
  30. const actor = statusDetails.actor ? (
  31. <strong>
  32. <UserAvatar user={statusDetails.actor} size={20} className="avatar" />
  33. <span style={{marginLeft: 5}}>{statusDetails.actor.name}</span>
  34. </strong>
  35. ) : null;
  36. const relevantActivity = activities.find(
  37. activity => activity.type === GroupActivityType.SET_RESOLVED_IN_RELEASE
  38. ) as
  39. | GroupActivitySetByResolvedInRelease
  40. | GroupActivitySetByResolvedInNextSemverRelease
  41. | undefined;
  42. if (statusDetails.inNextRelease) {
  43. // Resolved in next release has current_release_version (semver only)
  44. if (relevantActivity && 'current_release_version' in relevantActivity.data) {
  45. const version = (
  46. <Version
  47. version={relevantActivity.data.current_release_version}
  48. projectId={projectId}
  49. tooltipRawVersion
  50. />
  51. );
  52. return statusDetails.actor
  53. ? tct(
  54. '[actor] marked this issue as resolved in versions greater than [version].',
  55. {
  56. actor,
  57. version,
  58. }
  59. )
  60. : tct(
  61. 'This issue has been marked as resolved in versions greater than [version].',
  62. {version}
  63. );
  64. }
  65. return actor
  66. ? tct('[actor] marked this issue as resolved in the upcoming release.', {
  67. actor,
  68. })
  69. : t('This issue has been marked as resolved in the upcoming release.');
  70. }
  71. if (statusDetails.inRelease) {
  72. const version = (
  73. <Version
  74. version={statusDetails.inRelease}
  75. projectId={projectId}
  76. tooltipRawVersion
  77. />
  78. );
  79. return actor
  80. ? tct('[actor] marked this issue as resolved in version [version].', {
  81. actor,
  82. version,
  83. })
  84. : tct('This issue has been marked as resolved in version [version].', {version});
  85. }
  86. if (statusDetails.inCommit) {
  87. return tct('This issue has been marked as resolved by [commit]', {
  88. commit: (
  89. <Fragment>
  90. <CommitLink
  91. inline
  92. showIcon={false}
  93. commitId={statusDetails.inCommit.id}
  94. repository={statusDetails.inCommit.repository as Repository}
  95. />
  96. {statusDetails.inCommit.dateCreated && (
  97. <StyledTimeSince date={statusDetails.inCommit.dateCreated} />
  98. )}
  99. </Fragment>
  100. ),
  101. });
  102. }
  103. return t('This issue has been marked as resolved.');
  104. }
  105. function ResolutionBox({statusDetails, projectId, activities = []}: Props) {
  106. return (
  107. <BannerContainer priority="default">
  108. <BannerSummary>
  109. <StyledIconCheckmark color="successText" />
  110. <span>{renderReason(statusDetails, projectId, activities)}</span>
  111. </BannerSummary>
  112. </BannerContainer>
  113. );
  114. }
  115. const StyledTimeSince = styled(TimeSince)`
  116. color: ${p => p.theme.gray300};
  117. margin-left: ${space(0.5)};
  118. font-size: ${p => p.theme.fontSizeSmall};
  119. `;
  120. const StyledIconCheckmark = styled(IconCheckmark)`
  121. /* override margin defined in BannerSummary */
  122. margin-top: 0 !important;
  123. align-self: center;
  124. @media (max-width: ${p => p.theme.breakpoints.small}) {
  125. margin-top: ${space(0.5)} !important;
  126. align-self: flex-start;
  127. }
  128. `;
  129. export default ResolutionBox;