utils.tsx 5.6 KB

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