mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
154 lines
4.9 KiB
TypeScript
154 lines
4.9 KiB
TypeScript
import {useCallback} from 'react';
|
|
import ReactLazyLoad from 'react-lazyload';
|
|
import styled from '@emotion/styled';
|
|
|
|
import NegativeSpaceContainer from 'sentry/components/container/negativeSpaceContainer';
|
|
import ErrorBoundary from 'sentry/components/errorBoundary';
|
|
import {EventReplaySection} from 'sentry/components/events/eventReplay/eventReplaySection';
|
|
import LazyLoad from 'sentry/components/lazyLoad';
|
|
import LoadingIndicator from 'sentry/components/loadingIndicator';
|
|
import {ReplayGroupContextProvider} from 'sentry/components/replays/replayGroupContext';
|
|
import {replayBackendPlatforms} from 'sentry/data/platformCategories';
|
|
import {space} from 'sentry/styles/space';
|
|
import type {Group} from 'sentry/types';
|
|
import type {Event} from 'sentry/types/event';
|
|
import {getAnalyticsDataForEvent, getAnalyticsDataForGroup} from 'sentry/utils/events';
|
|
import {getReplayIdFromEvent} from 'sentry/utils/replays/getReplayIdFromEvent';
|
|
import {useHasOrganizationSentAnyReplayEvents} from 'sentry/utils/replays/hooks/useReplayOnboarding';
|
|
import {projectCanUpsellReplay} from 'sentry/utils/replays/projectSupportsReplay';
|
|
import useOrganization from 'sentry/utils/useOrganization';
|
|
import useProjectFromSlug from 'sentry/utils/useProjectFromSlug';
|
|
|
|
type Props = {
|
|
event: Event;
|
|
projectSlug: string;
|
|
group?: Group;
|
|
};
|
|
|
|
const CLIP_OFFSETS = {
|
|
durationAfterMs: 5_000,
|
|
durationBeforeMs: 5_000,
|
|
};
|
|
|
|
function EventReplayContent({
|
|
event,
|
|
group,
|
|
replayId,
|
|
}: Props & {replayId: undefined | string}) {
|
|
const organization = useOrganization();
|
|
const {hasOrgSentReplays, fetching} = useHasOrganizationSentAnyReplayEvents();
|
|
|
|
const replayOnboardingPanel = useCallback(
|
|
() => import('./replayInlineOnboardingPanel'),
|
|
[]
|
|
);
|
|
const replayPreview = useCallback(() => import('./replayPreview'), []);
|
|
const replayClipPreview = useCallback(() => import('./replayClipPreview'), []);
|
|
|
|
const hasReplayClipFeature = organization.features.includes(
|
|
'issue-details-inline-replay-viewer'
|
|
);
|
|
|
|
if (fetching) {
|
|
return null;
|
|
}
|
|
|
|
const platform = group?.project.platform ?? group?.platform ?? 'other';
|
|
const projectId = group?.project.id ?? event.projectID ?? '';
|
|
|
|
// frontend or backend platforms
|
|
if (!hasOrgSentReplays || (!replayId && replayBackendPlatforms.includes(platform))) {
|
|
return (
|
|
<ErrorBoundary mini>
|
|
<LazyLoad
|
|
component={replayOnboardingPanel}
|
|
platform={platform}
|
|
projectId={projectId}
|
|
/>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
|
|
if (!replayId) {
|
|
return null;
|
|
}
|
|
|
|
const startTimestampMS =
|
|
'startTimestamp' in event ? event.startTimestamp * 1000 : undefined;
|
|
const timeOfEvent = event.dateCreated ?? startTimestampMS ?? event.dateReceived;
|
|
const eventTimestampMs = timeOfEvent ? Math.floor(new Date(timeOfEvent).getTime()) : 0;
|
|
|
|
const commonProps = {
|
|
analyticsContext: 'issue_details',
|
|
replaySlug: replayId,
|
|
orgSlug: organization.slug,
|
|
eventTimestampMs,
|
|
fullReplayButtonProps: {
|
|
analyticsEventKey: 'issue_details.open_replay_details_clicked',
|
|
analyticsEventName: 'Issue Details: Open Replay Details Clicked',
|
|
analyticsParams: {
|
|
...getAnalyticsDataForEvent(event),
|
|
...getAnalyticsDataForGroup(group),
|
|
organization,
|
|
},
|
|
},
|
|
loadingFallback: (
|
|
<StyledNegativeSpaceContainer testId="replay-loading-placeholder">
|
|
<LoadingIndicator />
|
|
</StyledNegativeSpaceContainer>
|
|
),
|
|
};
|
|
|
|
return (
|
|
<ReplaySectionMinHeight>
|
|
<ErrorBoundary mini>
|
|
<ReplayGroupContextProvider groupId={group?.id} eventId={event.id}>
|
|
<ReactLazyLoad debounce={50} height={448} offset={0} once>
|
|
{hasReplayClipFeature ? (
|
|
<LazyLoad
|
|
{...commonProps}
|
|
component={replayClipPreview}
|
|
clipOffsets={CLIP_OFFSETS}
|
|
/>
|
|
) : (
|
|
<LazyLoad {...commonProps} component={replayPreview} />
|
|
)}
|
|
</ReactLazyLoad>
|
|
</ReplayGroupContextProvider>
|
|
</ErrorBoundary>
|
|
</ReplaySectionMinHeight>
|
|
);
|
|
}
|
|
|
|
export default function EventReplay({event, group, projectSlug}: Props) {
|
|
const organization = useOrganization();
|
|
const hasReplaysFeature = organization.features.includes('session-replay');
|
|
|
|
const project = useProjectFromSlug({organization, projectSlug});
|
|
const canUpsellReplay = projectCanUpsellReplay(project);
|
|
const replayId = getReplayIdFromEvent(event);
|
|
|
|
if (hasReplaysFeature && (replayId || canUpsellReplay)) {
|
|
return (
|
|
<EventReplayContent
|
|
event={event}
|
|
group={group}
|
|
projectSlug={projectSlug}
|
|
replayId={replayId}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// The min-height here is due to max-height that is set in replayPreview.tsx
|
|
const ReplaySectionMinHeight = styled(EventReplaySection)`
|
|
min-height: 508px;
|
|
`;
|
|
|
|
const StyledNegativeSpaceContainer = styled(NegativeSpaceContainer)`
|
|
height: 400px;
|
|
margin-bottom: ${space(2)};
|
|
`;
|