stackTraceMiniFrame.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ProjectAvatar from 'sentry/components/avatar/projectAvatar';
  4. import useStacktraceLink from 'sentry/components/events/interfaces/frame/useStacktraceLink';
  5. import ExternalLink from 'sentry/components/links/externalLink';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {Project} from 'sentry/types';
  9. import {getIntegrationIcon, getIntegrationSourceUrl} from 'sentry/utils/integrationUtil';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. import useProjects from 'sentry/utils/useProjects';
  12. import {useEventDetails} from 'sentry/views/starfish/queries/useEventDetails';
  13. interface Props {
  14. frame: Parameters<typeof useStacktraceLink>[0]['frame'];
  15. eventId?: string;
  16. projectId?: string;
  17. }
  18. export function StackTraceMiniFrame({frame, eventId, projectId}: Props) {
  19. const {projects} = useProjects();
  20. const project = projects.find(p => p.id === projectId);
  21. const {data: event} = useEventDetails({eventId, projectSlug: project?.slug});
  22. return (
  23. <FrameContainer>
  24. {project && (
  25. <ProjectAvatarContainer>
  26. <ProjectAvatar project={project} size={16} />
  27. </ProjectAvatarContainer>
  28. )}
  29. {frame.filename && <Emphasize>{frame?.filename}</Emphasize>}
  30. {frame.function && (
  31. <Fragment>
  32. <Deemphasize> {t('in')} </Deemphasize>
  33. <Emphasize>{frame?.function}</Emphasize>
  34. </Fragment>
  35. )}
  36. {frame.lineNo && (
  37. <Fragment>
  38. <Deemphasize> {t('at line')} </Deemphasize>
  39. <Emphasize>{frame?.lineNo}</Emphasize>
  40. </Fragment>
  41. )}
  42. {event && project && (
  43. <PushRight>
  44. <SourceCodeIntegrationLink event={event} project={project} frame={frame} />
  45. </PushRight>
  46. )}
  47. </FrameContainer>
  48. );
  49. }
  50. const FrameContainer = styled('div')`
  51. display: flex;
  52. align-items: center;
  53. gap: ${space(0.5)};
  54. padding: ${space(1.5)} ${space(2)};
  55. font-family: ${p => p.theme.text.family};
  56. font-size: ${p => p.theme.fontSizeMedium};
  57. border-top: 1px solid ${p => p.theme.border};
  58. background: ${p => p.theme.surface200};
  59. `;
  60. const ProjectAvatarContainer = styled('div')`
  61. margin-right: ${space(1)};
  62. `;
  63. const Emphasize = styled('span')`
  64. color: ${p => p.theme.gray500};
  65. `;
  66. const Deemphasize = styled('span')`
  67. color: ${p => p.theme.gray300};
  68. `;
  69. const PushRight = styled('span')`
  70. margin-left: auto;
  71. `;
  72. interface SourceCodeIntegrationLinkProps {
  73. event: Parameters<typeof useStacktraceLink>[0]['event'];
  74. frame: Parameters<typeof useStacktraceLink>[0]['frame'];
  75. project: Project;
  76. }
  77. function SourceCodeIntegrationLink({
  78. event,
  79. project,
  80. frame,
  81. }: SourceCodeIntegrationLinkProps) {
  82. const organization = useOrganization();
  83. const {data: match, isLoading} = useStacktraceLink({
  84. event,
  85. frame,
  86. orgSlug: organization.slug,
  87. projectSlug: project.slug,
  88. });
  89. if (match && match.config && match.sourceUrl && frame.lineNo && !isLoading) {
  90. return (
  91. <DeemphasizedExternalLink
  92. href={getIntegrationSourceUrl(
  93. match.config.provider.key,
  94. match.sourceUrl,
  95. frame.lineNo
  96. )}
  97. openInNewTab
  98. >
  99. <StyledIconWrapper>
  100. {getIntegrationIcon(match.config.provider.key, 'sm')}
  101. </StyledIconWrapper>
  102. {t('Open this line in %s', match.config.provider.name)}
  103. </DeemphasizedExternalLink>
  104. );
  105. }
  106. return null;
  107. }
  108. const DeemphasizedExternalLink = styled(ExternalLink)`
  109. display: flex;
  110. align-items: center;
  111. gap: ${space(0.75)};
  112. color: ${p => p.theme.gray300};
  113. `;
  114. const StyledIconWrapper = styled('span')`
  115. color: inherit;
  116. line-height: 0;
  117. `;