context.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import {
  2. createContext,
  3. type Dispatch,
  4. type Reducer,
  5. useCallback,
  6. useContext,
  7. useReducer,
  8. } from 'react';
  9. export const enum SectionKey {
  10. TRACE = 'trace',
  11. USER_FEEDBACK = 'user-feedback',
  12. LLM_MONITORING = 'llm-monitoring',
  13. UPTIME = 'uptime', // Only Uptime issues
  14. CRON = 'cron-timeline', // Only Cron issues
  15. HIGHLIGHTS = 'highlights',
  16. RESOURCES = 'resources', // Position controlled by flag
  17. EXCEPTION = 'exception',
  18. STACKTRACE = 'stacktrace',
  19. SPANS = 'spans',
  20. EVIDENCE = 'evidence',
  21. MESSAGE = 'message',
  22. SUSPECT_ROOT_CAUSE = 'suspect-root-cause',
  23. SPAN_EVIDENCE = 'span-evidence',
  24. HYDRATION_DIFF = 'hydration-diff',
  25. REPLAY = 'replay',
  26. HPKP = 'hpkp',
  27. CSP = 'csp',
  28. EXPECTCT = 'expectct',
  29. EXPECTSTAPLE = 'expectstaple',
  30. TEMPLATE = 'template',
  31. BREADCRUMBS = 'breadcrumbs',
  32. DEBUGMETA = 'debugmeta',
  33. REQUEST = 'request',
  34. TAGS = 'tags',
  35. SCREENSHOT = 'screenshot',
  36. CONTEXTS = 'contexts',
  37. EXTRA = 'extra',
  38. PACKAGES = 'packages',
  39. DEVICE = 'device',
  40. VIEW_HIERARCHY = 'view-hierarchy',
  41. ATTACHMENTS = 'attachments',
  42. SDK = 'sdk',
  43. GROUPING_INFO = 'grouping-info',
  44. PROCESSING_ERROR = 'processing-error',
  45. RRWEB = 'rrweb', // Legacy integration prior to replays
  46. }
  47. /**
  48. * This can be extended to create shared state for each section.
  49. * For example, if we needed to know the number of context cards we're rendering,
  50. * the <ContextDataSection /> can update the config for other components to read from.
  51. */
  52. export interface SectionConfig {
  53. key: SectionKey;
  54. initialCollapse?: boolean;
  55. }
  56. export interface EventDetailsContextType extends EventDetailsState {
  57. dispatch: Dispatch<EventDetailsActions>;
  58. }
  59. export const EventDetailsContext = createContext<EventDetailsContextType>({
  60. sectionData: {},
  61. dispatch: () => {},
  62. });
  63. export function useEventDetails() {
  64. return useContext(EventDetailsContext);
  65. }
  66. export interface EventDetailsState {
  67. sectionData: {
  68. [key in SectionKey]?: SectionConfig;
  69. };
  70. navScrollMargin?: number;
  71. }
  72. type UpdateSectionAction = {
  73. key: SectionKey;
  74. type: 'UPDATE_SECTION';
  75. config?: Partial<SectionConfig>;
  76. };
  77. type UpdateDetailsAction = {
  78. type: 'UPDATE_DETAILS';
  79. state?: Omit<EventDetailsState, 'sectionData'>;
  80. };
  81. export type EventDetailsActions = UpdateSectionAction | UpdateDetailsAction;
  82. function updateSection(
  83. state: EventDetailsState,
  84. sectionKey: SectionKey,
  85. updatedConfig: Partial<SectionConfig>
  86. ): EventDetailsState {
  87. const existingConfig = state.sectionData[sectionKey] ?? {key: sectionKey};
  88. const nextState: EventDetailsState = {
  89. ...state,
  90. sectionData: {
  91. ...state.sectionData,
  92. [sectionKey]: {...existingConfig, ...updatedConfig},
  93. },
  94. };
  95. return nextState;
  96. }
  97. /**
  98. * If trying to use the current state of the event page, you likely want to use `useEventDetails`
  99. * instead. This hook is just meant to create state for the provider.
  100. */
  101. export function useEventDetailsReducer() {
  102. const initialState: EventDetailsState = {
  103. sectionData: {},
  104. };
  105. const reducer: Reducer<EventDetailsState, EventDetailsActions> = useCallback(
  106. (state, action): EventDetailsState => {
  107. switch (action.type) {
  108. case 'UPDATE_SECTION':
  109. return updateSection(state, action.key, action.config ?? {});
  110. case 'UPDATE_DETAILS':
  111. return {...state, ...action.state};
  112. default:
  113. return state;
  114. }
  115. },
  116. []
  117. );
  118. const [eventDetails, dispatch] = useReducer(reducer, initialState);
  119. return {
  120. eventDetails,
  121. dispatch,
  122. };
  123. }