eventDetails.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import {useLayoutEffect, useState} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import ErrorBoundary from 'sentry/components/errorBoundary';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import type {Event} from 'sentry/types/event';
  8. import type {Group} from 'sentry/types/group';
  9. import {useIsStuck} from 'sentry/utils/useIsStuck';
  10. import useMedia from 'sentry/utils/useMedia';
  11. import {
  12. EventDetailsContent,
  13. type EventDetailsContentProps,
  14. } from 'sentry/views/issueDetails/groupEventDetails/groupEventDetailsContent';
  15. import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
  16. import {EventMissingBanner} from 'sentry/views/issueDetails/streamline/eventMissingBanner';
  17. import {EventTitle} from 'sentry/views/issueDetails/streamline/eventTitle';
  18. export function EventDetails({group, event, project}: EventDetailsContentProps) {
  19. if (!event) {
  20. return (
  21. <GroupContent role="main">
  22. <BannerPadding>
  23. <EventMissingBanner />
  24. </BannerPadding>
  25. </GroupContent>
  26. );
  27. }
  28. return (
  29. <PageErrorBoundary mini message={t('There was an error loading the event content')}>
  30. <GroupContent role="main">
  31. <StickyEventNav event={event} group={group} />
  32. <ContentPadding>
  33. <EventDetailsContent group={group} event={event} project={project} />
  34. </ContentPadding>
  35. </GroupContent>
  36. </PageErrorBoundary>
  37. );
  38. }
  39. function StickyEventNav({event, group}: {event: Event; group: Group}) {
  40. const theme = useTheme();
  41. const [nav, setNav] = useState<HTMLDivElement | null>(null);
  42. const isStuck = useIsStuck(nav);
  43. const isScreenMedium = useMedia(`(max-width: ${theme.breakpoints.medium})`);
  44. const {dispatch} = useIssueDetails();
  45. useLayoutEffect(() => {
  46. if (!nav) {
  47. return;
  48. }
  49. const navHeight = nav.offsetHeight ?? 0;
  50. const sidebarHeight = isScreenMedium ? theme.sidebar.mobileHeightNumber : 0;
  51. dispatch({
  52. type: 'UPDATE_NAV_SCROLL_MARGIN',
  53. margin: navHeight + sidebarHeight,
  54. });
  55. }, [nav, isScreenMedium, dispatch, theme.sidebar.mobileHeightNumber]);
  56. return (
  57. <FloatingEventNavigation
  58. event={event}
  59. group={group}
  60. ref={setNav}
  61. data-stuck={isStuck}
  62. />
  63. );
  64. }
  65. const FloatingEventNavigation = styled(EventTitle)`
  66. position: sticky;
  67. top: 0;
  68. @media (max-width: ${p => p.theme.breakpoints.medium}) {
  69. top: ${p => p.theme.sidebar.mobileHeight};
  70. }
  71. background: ${p => p.theme.background};
  72. z-index: ${p => p.theme.zIndex.header};
  73. border-radius: ${p => p.theme.borderRadiusTop};
  74. &[data-stuck='true'] {
  75. border-radius: 0;
  76. }
  77. `;
  78. const GroupContent = styled('div')`
  79. position: relative;
  80. border: 1px solid ${p => p.theme.translucentBorder};
  81. background: ${p => p.theme.background};
  82. border-radius: ${p => p.theme.borderRadius};
  83. `;
  84. const ContentPadding = styled('div')`
  85. padding: ${space(1)} ${space(1.5)};
  86. `;
  87. const BannerPadding = styled('div')`
  88. padding: 40px;
  89. `;
  90. const PageErrorBoundary = styled(ErrorBoundary)`
  91. margin: 0;
  92. border: 1px solid ${p => p.theme.translucentBorder};
  93. `;