import classNames from 'classnames'; import type { CSSProperties } from 'react'; import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { createGlobalState } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useLatestResponse } from '../hooks/useLatestResponse'; import { useResponseContentType } from '../hooks/useResponseContentType'; import { useResponses } from '../hooks/useResponses'; import { useResponseViewMode } from '../hooks/useResponseViewMode'; import type { HttpResponse } from '../lib/models'; import { isResponseLoading } from '../lib/models'; import { Banner } from './core/Banner'; import { CountBadge } from './core/CountBadge'; import { DurationTag } from './core/DurationTag'; import { HotKeyList } from './core/HotKeyList'; import { JsonAttributeTree } from './core/JsonAttributeTree'; import { SizeTag } from './core/SizeTag'; import { HStack } from './core/Stacks'; import { StatusTag } from './core/StatusTag'; import type { TabItem } from './core/Tabs/Tabs'; import { TabContent, Tabs } from './core/Tabs/Tabs'; import { EmptyStateText } from './EmptyStateText'; import { RecentResponsesDropdown } from './RecentResponsesDropdown'; import { ResponseHeaders } from './ResponseHeaders'; import { CsvViewer } from './responseViewers/CsvViewer'; import { ImageViewer } from './responseViewers/ImageViewer'; import { JsonViewer } from './responseViewers/JsonViewer'; import { TextViewer } from './responseViewers/TextViewer'; import { WebPageViewer } from './responseViewers/WebPageViewer'; interface Props { style?: CSSProperties; className?: string; } const useActiveTab = createGlobalState('body'); export const ResponsePane = memo(function ResponsePane({ style, className }: Props) { const [pinnedResponseId, setPinnedResponseId] = useState(null); const activeRequest = useActiveRequest(); const latestResponse = useLatestResponse(activeRequest?.id ?? null); const responses = useResponses(activeRequest?.id ?? null); const activeResponse: HttpResponse | null = pinnedResponseId ? responses.find((r) => r.id === pinnedResponseId) ?? null : latestResponse ?? null; const [viewMode, setViewMode] = useResponseViewMode(activeResponse?.requestId); const [activeTab, setActiveTab] = useActiveTab(); // Unset pinned response when a new one comes in useEffect(() => setPinnedResponseId(null), [responses.length]); const contentType = useResponseContentType(activeResponse); const handlePinnedResponse = useCallback( (r: HttpResponse) => { setPinnedResponseId(r.id); }, [setPinnedResponseId], ); const tabs = useMemo( () => [ { value: 'body', label: 'Preview', options: { value: viewMode, onChange: setViewMode, items: [ { label: 'Pretty', value: 'pretty' }, ...(contentType?.startsWith('image') ? [] : [{ label: 'Raw', value: 'raw' }]), ], }, }, { label: (
Headers h.name && h.value).length ?? 0} />
), value: 'headers', }, ], [activeResponse?.headers, contentType, setViewMode, viewMode], ); if (activeRequest === null) { return null; } return (
{activeResponse?.error && ( {activeResponse.error} )} {!activeResponse && ( <> )} {activeResponse && !activeResponse.error && !isResponseLoading(activeResponse) && ( <> {activeResponse && (
{activeResponse.elapsed > 0 && ( <> )} {!!activeResponse.contentLength && ( <> )}
)}
{!activeResponse.contentLength ? ( Empty Body ) : contentType?.startsWith('image') ? ( ) : activeResponse.contentLength > 2 * 1000 * 1000 ? (
Cannot preview text responses larger than 2MB
) : viewMode === 'pretty' && contentType?.includes('html') ? ( ) : contentType?.match(/csv|tab-separated/) ? ( ) : ( // ) : contentType?.startsWith('application/json') ? ( // )}
)}
); });