123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- import {
- createContext,
- type Dispatch,
- type Reducer,
- useCallback,
- useContext,
- useReducer,
- } from 'react';
- export const enum SectionKey {
- /**
- * Trace timeline or linked error
- */
- TRACE = 'trace',
- USER_FEEDBACK = 'user-feedback',
- LLM_MONITORING = 'llm-monitoring',
- UPTIME = 'uptime', // Only Uptime issues
- DOWNTIME = 'downtime',
- CRON_TIMELINE = 'cron-timeline', // Only Cron issues
- HIGHLIGHTS = 'highlights',
- RESOURCES = 'resources', // Position controlled by flag
- EXCEPTION = 'exception',
- STACKTRACE = 'stacktrace',
- THREADS = 'threads',
- SPANS = 'spans',
- EVIDENCE = 'evidence',
- MESSAGE = 'message',
- SUSPECT_ROOT_CAUSE = 'suspect-root-cause',
- SPAN_EVIDENCE = 'span-evidence',
- HYDRATION_DIFF = 'hydration-diff',
- REPLAY = 'replay',
- HPKP = 'hpkp',
- CSP = 'csp',
- EXPECTCT = 'expectct',
- EXPECTSTAPLE = 'expectstaple',
- TEMPLATE = 'template',
- BREADCRUMBS = 'breadcrumbs',
- /**
- * Also called images loaded
- */
- DEBUGMETA = 'debugmeta',
- REQUEST = 'request',
- TAGS = 'tags',
- SCREENSHOT = 'screenshot',
- FEATURE_FLAGS = 'feature-flags',
- CONTEXTS = 'contexts',
- EXTRA = 'extra',
- PACKAGES = 'packages',
- DEVICE = 'device',
- VIEW_HIERARCHY = 'view-hierarchy',
- ATTACHMENTS = 'attachments',
- SDK = 'sdk',
- GROUPING_INFO = 'grouping-info',
- PROCESSING_ERROR = 'processing-error',
- RRWEB = 'rrweb', // Legacy integration prior to replays
- MERGED_ISSUES = 'merged',
- SIMILAR_ISSUES = 'similar',
- REGRESSION_SUMMARY = 'regression-summary',
- REGRESSION_BREAKPOINT_CHART = 'regression-breakpoint-chart',
- REGRESSION_FLAMEGRAPH = 'regression-flamegraph',
- REGRESSION_PROFILE_COMPARISON = 'regression-profile-comparison',
- REGRESSION_EVENT_COMPARISON = 'regression-event-comparison',
- REGRESSION_POTENTIAL_CAUSES = 'regression-potential-causes',
- REGRESSION_AFFECTED_TRANSACTIONS = 'regression-affected-transactions',
- }
- /**
- * This can be extended to create shared state for each section.
- * For example, if we needed to know the number of context cards we're rendering,
- * the <ContextDataSection /> can update the config for other components to read from.
- */
- export interface SectionConfig {
- key: SectionKey;
- initialCollapse?: boolean;
- }
- export interface IssueDetailsContextType extends IssueDetailsState {
- dispatch: Dispatch<IssueDetailsActions>;
- }
- export const IssueDetailsContext = createContext<IssueDetailsContextType>({
- sectionData: {},
- isSidebarOpen: true,
- navScrollMargin: 0,
- eventCount: 0,
- dispatch: () => {},
- });
- export function useIssueDetails() {
- return useContext(IssueDetailsContext);
- }
- export interface IssueDetailsState {
- /**
- * Allows updating the event count based on the date/time/environment filters.
- */
- eventCount: number;
- /**
- * Controls whether the sidebar is open.
- */
- isSidebarOpen: boolean;
- /**
- * The margin to add to the 'Jump To' nav (accounts for the main app sidebar on small screen sizes).
- */
- navScrollMargin: number;
- /**
- * Controls the state of each section.
- */
- sectionData: {
- [key in SectionKey]?: SectionConfig;
- };
- }
- type UpdateEventSectionAction = {
- key: SectionKey;
- type: 'UPDATE_EVENT_SECTION';
- config?: Partial<SectionConfig>;
- };
- type UpdateNavScrollMarginAction = {
- margin: number;
- type: 'UPDATE_NAV_SCROLL_MARGIN';
- };
- type UpdateEventCountAction = {
- count: number;
- type: 'UPDATE_EVENT_COUNT';
- };
- type UpdateSidebarAction = {
- isOpen: boolean;
- type: 'UPDATE_SIDEBAR_STATE';
- };
- export type IssueDetailsActions =
- | UpdateEventSectionAction
- | UpdateNavScrollMarginAction
- | UpdateEventCountAction
- | UpdateSidebarAction;
- function updateEventSection(
- state: IssueDetailsState,
- sectionKey: SectionKey,
- updatedConfig: Partial<SectionConfig>
- ): IssueDetailsState {
- const existingConfig = state.sectionData[sectionKey] ?? {key: sectionKey};
- const nextState: IssueDetailsState = {
- ...state,
- sectionData: {
- ...state.sectionData,
- [sectionKey]: {...existingConfig, ...updatedConfig},
- },
- };
- return nextState;
- }
- /**
- * If trying to use the current state of the issue/event page, you likely want to use
- * `useIssueDetails` instead. This hook is just meant to create state for the provider.
- */
- export function useIssueDetailsReducer() {
- const initialState: IssueDetailsState = {
- sectionData: {},
- isSidebarOpen: true,
- eventCount: 0,
- navScrollMargin: 0,
- };
- const reducer: Reducer<IssueDetailsState, IssueDetailsActions> = useCallback(
- (state, action): IssueDetailsState => {
- switch (action.type) {
- case 'UPDATE_SIDEBAR_STATE':
- return {...state, isSidebarOpen: action.isOpen};
- case 'UPDATE_NAV_SCROLL_MARGIN':
- return {...state, navScrollMargin: action.margin};
- case 'UPDATE_EVENT_SECTION':
- return updateEventSection(state, action.key, action.config ?? {});
- case 'UPDATE_EVENT_COUNT':
- return {...state, eventCount: action.count};
- default:
- return state;
- }
- },
- []
- );
- const [issueDetails, dispatch] = useReducer(reducer, initialState);
- return {
- issueDetails,
- dispatch,
- };
- }
|