import {MouseEvent, useEffect, useMemo} from 'react';
import queryString from 'query-string';
import ObjectInspector from 'sentry/components/objectInspector';
import {useReplayContext} from 'sentry/components/replays/replayContext';
import {t} from 'sentry/locale';
import {formatBytesBase10} from 'sentry/utils';
import {
getFrameMethod,
getFrameStatus,
isRequestFrame,
} from 'sentry/utils/replays/resourceFrame';
import type {SpanFrame} from 'sentry/utils/replays/types';
import {
Indent,
keyValueTableOrNotFound,
SectionItem,
SizeTooltip,
Warning,
} from 'sentry/views/replays/detail/network/details/components';
import {useDismissReqRespBodiesAlert} from 'sentry/views/replays/detail/network/details/onboarding';
import TimestampButton from 'sentry/views/replays/detail/timestampButton';
export type SectionProps = {
item: SpanFrame;
projectId: string;
startTimestampMs: number;
};
const UNKNOWN_STATUS = 'unknown';
export function GeneralSection({item, startTimestampMs}: SectionProps) {
const {setCurrentTime} = useReplayContext();
const requestFrame = isRequestFrame(item) ? item : null;
// TODO[replay]: what about:
// `requestFrame?.data?.request?.size` vs. `requestFrame?.data?.requestBodySize`
const data = {
[t('URL')]: item.description,
[t('Type')]: item.op,
[t('Method')]: getFrameMethod(item),
[t('Status Code')]: String(getFrameStatus(item) ?? UNKNOWN_STATUS),
[t('Request Body Size')]: (
{formatBytesBase10(requestFrame?.data?.request?.size ?? 0)}
),
[t('Response Body Size')]: (
{formatBytesBase10(requestFrame?.data?.response?.size ?? 0)}
),
[t('Duration')]: `${(item.endTimestampMs - item.timestampMs).toFixed(2)}ms`,
[t('Timestamp')]: (
{
event.stopPropagation();
setCurrentTime(item.offsetMs);
}}
startTimestampMs={startTimestampMs}
timestampMs={item.timestampMs}
/>
),
};
return (
{keyValueTableOrNotFound(data, t('Missing request details'))}
);
}
export function RequestHeadersSection({item}: SectionProps) {
const data = isRequestFrame(item) ? item.data : {};
return (
{keyValueTableOrNotFound(data.request?.headers, t('Headers not captured'))}
);
}
export function ResponseHeadersSection({item}: SectionProps) {
const data = isRequestFrame(item) ? item.data : {};
return (
{keyValueTableOrNotFound(data.response?.headers, t('Headers not captured'))}
);
}
export function QueryParamsSection({item}: SectionProps) {
const queryParams = queryString.parse(item.description?.split('?')?.[1] ?? '');
return (
);
}
export function RequestPayloadSection({item}: SectionProps) {
const {dismiss, isDismissed} = useDismissReqRespBodiesAlert();
const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]);
useEffect(() => {
if (!isDismissed && 'request' in data) {
dismiss();
}
}, [dismiss, data, isDismissed]);
return (
{t('Size:')} {formatBytesBase10(data.request?.size ?? 0)}
}
>
{'request' in data ? (
) : (
t('Request body not found.')
)}
);
}
export function ResponsePayloadSection({item}: SectionProps) {
const {dismiss, isDismissed} = useDismissReqRespBodiesAlert();
const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]);
useEffect(() => {
if (!isDismissed && 'response' in data) {
dismiss();
}
}, [dismiss, data, isDismissed]);
return (
{t('Size:')} {formatBytesBase10(data.response?.size ?? 0)}
}
>
{'response' in data ? (
) : (
t('Response body not found.')
)}
);
}