import {Fragment, useEffect} from 'react';
import type {RouteComponentProps} from 'react-router';
import Alert from 'sentry/components/alert';
import {Flex} from 'sentry/components/container/flex';
import DetailedError from 'sentry/components/errors/detailedError';
import NotFound from 'sentry/components/errors/notFound';
import * as Layout from 'sentry/components/layouts/thirds';
import List from 'sentry/components/list';
import ListItem from 'sentry/components/list/listItem';
import {LocalStorageReplayPreferences} from 'sentry/components/replays/preferences/replayPreferences';
import {Provider as ReplayContextProvider} from 'sentry/components/replays/replayContext';
import {IconDelete} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {decodeScalar} from 'sentry/utils/queryString';
import type {TimeOffsetLocationQueryParams} from 'sentry/utils/replays/hooks/useInitialTimeOffsetMs';
import useInitialTimeOffsetMs from 'sentry/utils/replays/hooks/useInitialTimeOffsetMs';
import useLogReplayDataLoaded from 'sentry/utils/replays/hooks/useLogReplayDataLoaded';
import useMarkReplayViewed from 'sentry/utils/replays/hooks/useMarkReplayViewed';
import useReplayPageview from 'sentry/utils/replays/hooks/useReplayPageview';
import useReplayReader from 'sentry/utils/replays/hooks/useReplayReader';
import useRouteAnalyticsEventNames from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames';
import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {useUser} from 'sentry/utils/useUser';
import ReplaysLayout from 'sentry/views/replays/detail/layout';
import Page from 'sentry/views/replays/detail/page';
import ReplayTransactionContext from 'sentry/views/replays/detail/trace/replayTransactionContext';
type Props = RouteComponentProps<
{replaySlug: string},
{},
any,
TimeOffsetLocationQueryParams
>;
function ReplayDetails({params: {replaySlug}}: Props) {
const user = useUser();
const location = useLocation();
const organization = useOrganization();
useReplayPageview('replay.details-time-spent');
useRouteAnalyticsEventNames('replay_details.viewed', 'Replay Details: Viewed');
useRouteAnalyticsParams({
organization,
referrer: decodeScalar(location.query.referrer),
user_email: user.email,
tab: location.query.t_main,
});
const {slug: orgSlug} = organization;
// TODO: replayId is known ahead of time and useReplayData is parsing it from the replaySlug
// once we fix the route params and links we should fix this to accept replayId and stop returning it
const {
errors,
fetchError,
fetching,
onRetry,
projectSlug,
replay,
replayId,
replayRecord,
} = useReplayReader({
replaySlug,
orgSlug,
});
const replayErrors = errors.filter(e => e.title !== 'User Feedback');
useLogReplayDataLoaded({fetchError, fetching, projectSlug, replay});
const {mutate: markAsViewed} = useMarkReplayViewed();
useEffect(() => {
if (
!fetchError &&
replayRecord &&
!replayRecord.has_viewed &&
projectSlug &&
!fetching &&
replayId
) {
markAsViewed({projectSlug, replayId});
}
}, [
fetchError,
fetching,
markAsViewed,
organization,
projectSlug,
replayId,
replayRecord,
]);
const initialTimeOffsetMs = useInitialTimeOffsetMs({
orgSlug,
projectSlug,
replayId,
replayStartTimestampMs: replayRecord?.started_at?.getTime(),
});
const rrwebFrames = replay?.getRRWebFrames();
// The replay data takes a while to load in, which causes `isVideoReplay`
// to return an early `false`, which used to cause UI jumping.
// One way to check whether it's finished loading is by checking the length
// of the rrweb frames, which should always be > 2 for any given replay.
// By default, the 2 frames are replay.start and replay.end
const isLoading = !rrwebFrames || (rrwebFrames && rrwebFrames.length <= 2);
if (replayRecord?.is_archived) {
return (
{t('This replay has been deleted.')}
);
}
if (fetchError) {
if (fetchError.status === 404) {
return (
);
}
const reasons = [
t('The replay is still processing'),
t('The replay has been deleted by a member in your organization'),
t('There is an internal systems error'),
];
return (