index.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import styled from '@emotion/styled';
  2. import {t} from 'sentry/locale';
  3. import space from 'sentry/styles/space';
  4. import {Event} from 'sentry/types/event';
  5. import {objectIsEmpty} from 'sentry/utils';
  6. import {ContextSummaryDevice} from './contextSummaryDevice';
  7. import {ContextSummaryGeneric} from './contextSummaryGeneric';
  8. import {ContextSummaryGPU} from './contextSummaryGPU';
  9. import {ContextSummaryOS} from './contextSummaryOS';
  10. import {ContextSummaryUser} from './contextSummaryUser';
  11. import filterContexts from './filterContexts';
  12. export type Context = {
  13. // TODO(ts): Refactor this component
  14. Component: (props: any) => JSX.Element;
  15. keys: string[];
  16. omitUnknownVersion?: boolean;
  17. unknownTitle?: string;
  18. };
  19. const MIN_CONTEXTS = 3;
  20. const MAX_CONTEXTS = 4;
  21. const KNOWN_CONTEXTS: Context[] = [
  22. {keys: ['user'], Component: ContextSummaryUser},
  23. {
  24. keys: ['browser'],
  25. Component: ContextSummaryGeneric,
  26. unknownTitle: t('Unknown Browser'),
  27. },
  28. {
  29. keys: ['runtime'],
  30. Component: ContextSummaryGeneric,
  31. unknownTitle: t('Unknown Runtime'),
  32. omitUnknownVersion: true,
  33. },
  34. {keys: ['client_os', 'os'], Component: ContextSummaryOS},
  35. {keys: ['device'], Component: ContextSummaryDevice},
  36. {keys: ['gpu'], Component: ContextSummaryGPU},
  37. ];
  38. type Props = {
  39. event: Event;
  40. };
  41. function ContextSummary({event}: Props) {
  42. let contextCount = 0;
  43. // Add defined contexts in the declared order, until we reach the limit
  44. // defined by MAX_CONTEXTS.
  45. let contexts = KNOWN_CONTEXTS.filter(context => filterContexts(event, context)).map(
  46. ({keys, Component, unknownTitle, omitUnknownVersion}) => {
  47. if (contextCount >= MAX_CONTEXTS) {
  48. return null;
  49. }
  50. const [key, data] = keys
  51. .map(k => [k, event.contexts[k] || event[k]])
  52. .find(([_k, d]) => !objectIsEmpty(d)) || [null, null];
  53. if (!key) {
  54. return null;
  55. }
  56. contextCount += 1;
  57. return (
  58. <Component
  59. key={key}
  60. data={data}
  61. unknownTitle={unknownTitle}
  62. omitUnknownVersion={omitUnknownVersion}
  63. meta={event._meta?.contexts?.[key] ?? {}}
  64. />
  65. );
  66. }
  67. );
  68. // Bail out if all contexts are empty or only the user context is set
  69. if (contextCount === 0 || (contextCount === 1 && contexts[0])) {
  70. return null;
  71. }
  72. if (contextCount < MIN_CONTEXTS) {
  73. // Add contents in the declared order until we have at least MIN_CONTEXTS
  74. // contexts in our list.
  75. contexts = KNOWN_CONTEXTS.filter(context => filterContexts(event, context)).map(
  76. ({keys, Component, unknownTitle}, index) => {
  77. if (contexts[index]) {
  78. return contexts[index];
  79. }
  80. if (contextCount >= MIN_CONTEXTS) {
  81. return null;
  82. }
  83. contextCount += 1;
  84. return (
  85. <Component
  86. key={keys[0]}
  87. data={{}}
  88. unknownTitle={unknownTitle}
  89. meta={event._meta?.contexts?.[keys[0]] ?? {}}
  90. />
  91. );
  92. }
  93. );
  94. }
  95. return <Wrapper className="context-summary">{contexts}</Wrapper>;
  96. }
  97. export default ContextSummary;
  98. const Wrapper = styled('div')`
  99. @media (min-width: ${p => p.theme.breakpoints.small}) {
  100. display: flex;
  101. gap: ${space(3)};
  102. margin-bottom: ${space(2)};
  103. }
  104. `;