resolutionBox.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. commitId={statusDetails.inCommit.id}
  92. repository={statusDetails.inCommit.repository as Repository}
  93. />
  94. {statusDetails.inCommit.dateCreated && (
  95. <StyledTimeSince date={statusDetails.inCommit.dateCreated} />
  96. )}
  97. </Fragment>
  98. ),
  99. });
  100. }
  101. return t('This issue has been marked as resolved.');
  102. }
  103. function ResolutionBox({statusDetails, projectId, activities = []}: Props) {
  104. return (
  105. <BannerContainer priority="default">
  106. <BannerSummary>
  107. <StyledIconCheckmark color="successText" />
  108. <span>{renderReason(statusDetails, projectId, activities)}</span>
  109. </BannerSummary>
  110. </BannerContainer>
  111. );
  112. }
  113. const StyledTimeSince = styled(TimeSince)`
  114. color: ${p => p.theme.gray300};
  115. margin-left: ${space(0.5)};
  116. font-size: ${p => p.theme.fontSizeSmall};
  117. `;
  118. const StyledIconCheckmark = styled(IconCheckmark)`
  119. /* override margin defined in BannerSummary */
  120. margin-top: 0 !important;
  121. align-self: center;
  122. @media (max-width: ${p => p.theme.breakpoints.small}) {
  123. margin-top: ${space(0.5)} !important;
  124. align-self: flex-start;
  125. }
  126. `;
  127. export default ResolutionBox;