utils.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import {useMemo} from 'react';
  2. import isEmpty from 'lodash/isEmpty';
  3. import orderBy from 'lodash/orderBy';
  4. import {bulkUpdate, useFetchIssueTags} from 'sentry/actionCreators/group';
  5. import {Client} from 'sentry/api';
  6. import {t} from 'sentry/locale';
  7. import {Group, GroupActivity} from 'sentry/types';
  8. import {Event} from 'sentry/types/event';
  9. import {useLocation} from 'sentry/utils/useLocation';
  10. export function markEventSeen(
  11. api: Client,
  12. orgId: string,
  13. projectId: string,
  14. groupId: string
  15. ) {
  16. bulkUpdate(
  17. api,
  18. {
  19. orgId,
  20. projectId,
  21. itemIds: [groupId],
  22. failSilently: true,
  23. data: {hasSeen: true},
  24. },
  25. {}
  26. );
  27. }
  28. export function fetchGroupUserReports(groupId: string, query: Record<string, string>) {
  29. const api = new Client();
  30. return api.requestPromise(`/issues/${groupId}/user-reports/`, {
  31. includeAllArgs: true,
  32. query,
  33. });
  34. }
  35. /**
  36. * Returns the environment name for an event or null
  37. *
  38. * @param event
  39. */
  40. export function getEventEnvironment(event: Event) {
  41. const tag = event.tags.find(({key}) => key === 'environment');
  42. return tag ? tag.value : null;
  43. }
  44. const SUBSCRIPTION_REASONS = {
  45. commented: t(
  46. "You're receiving workflow notifications because you have commented on this issue."
  47. ),
  48. assigned: t(
  49. "You're receiving workflow notifications because you were assigned to this issue."
  50. ),
  51. bookmarked: t(
  52. "You're receiving workflow notifications because you have bookmarked this issue."
  53. ),
  54. changed_status: t(
  55. "You're receiving workflow notifications because you have changed the status of this issue."
  56. ),
  57. mentioned: t(
  58. "You're receiving workflow notifications because you have been mentioned in this issue."
  59. ),
  60. };
  61. /**
  62. * @param group
  63. * @param removeLinks add/remove links to subscription reasons text (default: false)
  64. * @returns Reason for subscription
  65. */
  66. export function getSubscriptionReason(group: Group) {
  67. if (group.subscriptionDetails && group.subscriptionDetails.disabled) {
  68. return t('You have disabled workflow notifications for this project.');
  69. }
  70. if (!group.isSubscribed) {
  71. return t('Subscribe to workflow notifications for this issue');
  72. }
  73. if (group.subscriptionDetails) {
  74. const {reason} = group.subscriptionDetails;
  75. if (reason === 'unknown') {
  76. return t(
  77. "You're receiving workflow notifications because you are subscribed to this issue."
  78. );
  79. }
  80. if (reason && SUBSCRIPTION_REASONS.hasOwnProperty(reason)) {
  81. return SUBSCRIPTION_REASONS[reason];
  82. }
  83. }
  84. return t(
  85. "You're receiving updates because you are subscribed to workflow notifications for this project."
  86. );
  87. }
  88. export function getGroupMostRecentActivity(activities: GroupActivity[]) {
  89. // Most recent activity
  90. return orderBy([...activities], ({dateCreated}) => new Date(dateCreated), ['desc'])[0];
  91. }
  92. export enum ReprocessingStatus {
  93. REPROCESSED_AND_HASNT_EVENT = 'reprocessed_and_hasnt_event',
  94. REPROCESSED_AND_HAS_EVENT = 'reprocessed_and_has_event',
  95. REPROCESSING = 'reprocessing',
  96. NO_STATUS = 'no_status',
  97. }
  98. // Reprocessing Checks
  99. export function getGroupReprocessingStatus(
  100. group: Group,
  101. mostRecentActivity?: GroupActivity
  102. ) {
  103. const {status, count, activity: activities} = group;
  104. const groupCount = Number(count);
  105. switch (status) {
  106. case 'reprocessing':
  107. return ReprocessingStatus.REPROCESSING;
  108. case 'unresolved': {
  109. const groupMostRecentActivity =
  110. mostRecentActivity ?? getGroupMostRecentActivity(activities);
  111. if (groupMostRecentActivity?.type === 'reprocess') {
  112. if (groupCount === 0) {
  113. return ReprocessingStatus.REPROCESSED_AND_HASNT_EVENT;
  114. }
  115. return ReprocessingStatus.REPROCESSED_AND_HAS_EVENT;
  116. }
  117. return ReprocessingStatus.NO_STATUS;
  118. }
  119. default:
  120. return ReprocessingStatus.NO_STATUS;
  121. }
  122. }
  123. export const useFetchIssueTagsForDetailsPage = (
  124. {
  125. groupId,
  126. environment = [],
  127. }: {
  128. environment: string[];
  129. groupId?: string;
  130. },
  131. {enabled = true}: {enabled?: boolean} = {}
  132. ) => {
  133. return useFetchIssueTags(
  134. {
  135. groupId,
  136. environment,
  137. readable: true,
  138. limit: 4,
  139. },
  140. {enabled}
  141. );
  142. };
  143. export function useEnvironmentsFromUrl(): string[] {
  144. const location = useLocation();
  145. const envs = location.query.environment;
  146. const envsArray = useMemo(() => {
  147. return typeof envs === 'string' ? [envs] : envs ?? [];
  148. }, [envs]);
  149. return envsArray;
  150. }
  151. export function getGroupDetailsQueryData({
  152. environments,
  153. }: {
  154. environments?: string[];
  155. } = {}): Record<string, string | string[]> {
  156. // Note, we do not want to include the environment key at all if there are no environments
  157. const query: Record<string, string | string[]> = {
  158. ...(!isEmpty(environments) ? {environment: environments} : {}),
  159. expand: ['inbox', 'owners'],
  160. collapse: ['release', 'tags'],
  161. };
  162. return query;
  163. }
  164. export function getGroupEventDetailsQueryData({
  165. environments,
  166. stacktraceOnly,
  167. }: {
  168. environments?: string[];
  169. stacktraceOnly?: boolean;
  170. } = {}): Record<string, string | string[]> {
  171. const defaultParams = {collapse: stacktraceOnly ? ['stacktraceOnly'] : ['fullRelease']};
  172. if (!environments || isEmpty(environments)) {
  173. return defaultParams;
  174. }
  175. return {...defaultParams, environment: environments};
  176. }