import classNames from 'classnames'; import type { ReactNode } from 'react'; import { useCallback, useMemo } from 'react'; import { createGlobalState } from 'react-use'; import { useContentTypeFromHeaders } from '../../hooks/useContentTypeFromHeaders'; import { useDebouncedValue } from '../../hooks/useDebouncedValue'; import { useFilterResponse } from '../../hooks/useFilterResponse'; import { useResponseBodyText } from '../../hooks/useResponseBodyText'; import { tryFormatJson, tryFormatXml } from '../../lib/formatters'; import type { HttpResponse } from '../../lib/models'; import { Editor } from '../core/Editor'; import { hyperlink } from '../core/Editor/hyperlink/extension'; import { IconButton } from '../core/IconButton'; import { Input } from '../core/Input'; const extraExtensions = [hyperlink]; interface Props { response: HttpResponse; pretty: boolean; className?: string; } const useFilterText = createGlobalState>({}); export function TextViewer({ response, pretty, className }: Props) { const [filterTextMap, setFilterTextMap] = useFilterText(); const filterText = filterTextMap[response.id] ?? null; const debouncedFilterText = useDebouncedValue(filterText, 300); const setFilterText = useCallback( (v: string | null) => { setFilterTextMap((m) => ({ ...m, [response.id]: v })); }, [setFilterTextMap, response], ); const contentType = useContentTypeFromHeaders(response.headers); const rawBody = useResponseBodyText(response) ?? ''; const isSearching = filterText != null; const formattedBody = pretty && contentType?.includes('json') ? tryFormatJson(rawBody) : pretty && contentType?.includes('xml') ? tryFormatXml(rawBody) : rawBody; const filteredResponse = useFilterResponse({ filter: debouncedFilterText ?? '', responseId: response.id, }); const body = isSearching && filterText?.length > 0 ? filteredResponse : formattedBody; const toggleSearch = useCallback(() => { if (isSearching) { setFilterText(null); } else { setFilterText(''); } }, [isSearching, setFilterText]); const isJson = contentType?.includes('json'); const isXml = contentType?.includes('xml') || contentType?.includes('html'); const canFilter = isJson || isXml; const actions = useMemo(() => { const result: ReactNode[] = []; if (!canFilter) return result; if (isSearching) { result.push(
e.key === 'Escape' && toggleSearch()} onChange={setFilterText} />
, ); } result.push( , ); return result; }, [canFilter, filterText, isJson, isSearching, setFilterText, toggleSearch]); return ( ); }